ANdroid O MeidiaPlayer 深入理解一()
前言
android對於java層的音訊播放器提供了很多api,主要的有 AudioTrack、SoundPool、MediaPlayer(其實AudioPlayer和MediaPlayerAdapter也都是加了AudioFcus後對於MediaPlayer的二次封裝,關於AudioFocus有時間再詳細介紹)。其中AudioTrack主要是播放pcm流,而soundPool主要播放一些短暫的聲音,比如touch音。MediaPlayer主要播放媒體音訊檔案像.mp3檔案等。其中SoundPool和MediaPlayer最終都會在native層呼叫到audioTrack將音訊流寫入到對應devices上。關於MediaPlayer和AudioTrack的區別,這裡就不細說了,百度一大堆。主要就是MediaPlayer會把.mp3等格式檔案最終解析成pcm流輸出給audiotrack。關於MediaPlayer的Java層的各狀態轉化和各方法呼叫說明,這裡也不一一細說了。度娘全是這玩意。這裡主要說說MediaPlayer關於native層的東東(本人java出生,對於c/c++等了解的難免疏漏,如有理解錯誤,望各位大神不吝賜教,定虛心改正)。
初始化
給大家提供一個免費看原始碼的網站(知道請略過)http://androidxref.com/言歸正傳,就從java層的MediaPlayer說起吧。
- 構造方法
MediaPlayer位於frameworks/base/media/java/android/media/下繼承PlayerBase。其中PlayerBase的構造方法如下:
PlayerBase(@NonNull AudioAttributes attr, int implType) { if (attr == null) { throw new IllegalArgumentException("Illegal null AudioAttributes"); } mAttributes = attr; mImplType = implType; mState = AudioPlaybackConfiguration.PLAYER_STATE_IDLE; };
MediaPlayer的構造方法:
public MediaPlayer() { super(new AudioAttributes.Builder().build(), AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER); Looper looper; if ((looper = Looper.myLooper()) != null) { mEventHandler = new EventHandler(this, looper); } else if ((looper = Looper.getMainLooper()) != null) { mEventHandler = new EventHandler(this, looper); } else { mEventHandler = null; } mTimeProvider = new TimeProvider(this); mOpenSubtitleSources = new Vector<InputStream>(); /* Native setup requires a weak reference to our object. * It's easier to create it here than in C++. */ native_setup(new WeakReference<MediaPlayer>(this)); baseRegisterPlayer(); }
可以看我們在使用時MediaPlayer時,通過new MediaPlayer的方式到實際都做了什麼,著重關注下這幾個點1.new AudioAttributes.Builder()//這個主要後面的audioPolicy會用到。2.native_setup3.baseRegisterPlayer註冊了一個player狀態回撥,這塊邏輯,感興趣的可以自己查下原始碼,暫忽略掉,有時間細看再補上這塊吧,今天重點不是他。重點說2.native_setup,這步直接呼叫了jni方法,關於jni我瞭解不是很多,我主要做從事App開發的,底層的東東只能略知一二,說的不對的,請多多指教。
private native final void native_setup(Object mediaplayer_this);
jni的載入主要通過 System.loadLibrary來實現的:
static {
System.loadLibrary("media_jni");
native_init();
}
在/frameworks/base/media/jni/android_media_MediaPlayer.cpp目錄下,這樣就走到了C++部分。通過JNINativeMethod gMethods[]方法知道native_setup會調到android_media_MediaPlayer_native_setup方法。
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup},
其中:
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
ALOGV("native_setup");
sp<MediaPlayer> mp = new MediaPlayer();
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
// create new listener and give it to MediaPlayer
sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
mp->setListener(listener);
// Stow our new C++ MediaPlayer in an opaque field in the Java object.
setMediaPlayer(env, thiz, mp);
}
Ok分析下sp<MediaPlayer> mp = new MediaPlayer(); native層的mediaplayer也有了,建立了一個C++的mediaPlayer物件。繼續:setMediaPlayer(env, thiz, mp)將建立的Native層的MediaPlayer物件儲存到Java層。也就是說將來我們通過getMediaplayer()的時候獲取到的就是這個物件。
總結
到此MediaPlayer就建立完成了,通過java程式碼 new MediaPlayer()開始,一直到native層建立native層的MediaPlayer,並將native層的MediaPlayer返回到java層,供java層呼叫。其實整個MediaPlayer在執行的時候,可以大致上分成Client和Server兩個部分,它們分別在兩個程序中執行,它們之間使用Binder機制實現IPC通訊,但Client端分一個在java層的MediaPlayer和native層的MediaPlayer。最後感謝百度各位大師提供資料。