在《Binder簡(jiǎn)介》一篇中,我們了解了Binder進(jìn)程間通訊的大致執(zhí)行原理掏击,從這一篇開(kāi)始耳胎,通過(guò)分析源碼來(lái)認(rèn)識(shí)Binder底層的調(diào)用過(guò)程心褐。
Binder結(jié)構(gòu)
Binder采用C/S結(jié)構(gòu)的通訊方式,提供服務(wù)的為Server進(jìn)程怕午,訪問(wèn)的進(jìn)程為Client進(jìn)程炫刷,一個(gè)Server進(jìn)程可以運(yùn)行多個(gè)組件來(lái)為Client服務(wù),這些組件稱為Service組件籍铁。Client和Server進(jìn)程都維護(hù)了自己的一個(gè)Binder線程池涡上,因此,Client可以并發(fā)訪問(wèn)拒名,Server也可以并發(fā)提供服務(wù)吩愧。每個(gè)Service組件在啟動(dòng)時(shí),都會(huì)添加進(jìn)ServiceManager中增显,它用來(lái)對(duì)組件進(jìn)行統(tǒng)一管理雁佳。Client和Service組件由應(yīng)用程序?qū)崿F(xiàn)。ServiceManager和Binder驅(qū)動(dòng)由系統(tǒng)實(shí)現(xiàn)。
/dev/binder是Binder驅(qū)動(dòng)對(duì)外暴露的設(shè)備文件糖权,Client和Server進(jìn)程堵腹,通過(guò)系統(tǒng)調(diào)用open、mmap和ioctl來(lái)訪問(wèn)/dev/binder文件星澳,進(jìn)而訪問(wèn)Binder驅(qū)動(dòng)疚顷。我們知道,在Linux中募判,通過(guò)操作文件的方式荡含,便能間接控制設(shè)備,因此届垫,操作/dev/binder文件释液,就相當(dāng)于在操作Binder驅(qū)動(dòng)了。
MediaPlayer
之所以選擇MediaPlayer作為理解Binder的切入點(diǎn)装处,原因有二:一是媒體播放這種需求比較常見(jiàn)误债;二是,MediaPlayer (Client)需要通過(guò)底層MediaServer(Server)管理運(yùn)行的組件MediaPlayerService妄迁,來(lái)實(shí)現(xiàn)上層播放功能寝蹈,而MediaServer包含了許多重要的Service組件,如:
- AudioFlinger:音頻系統(tǒng)中的核心服務(wù)登淘。
- AudioPolicyService:音頻系統(tǒng)中關(guān)于音頻策略的重要任務(wù)箫老。
- CameraService:攝像/照相的重要服務(wù)。
- MediaPlayerService:多媒體系統(tǒng)中的重要服務(wù)黔州。
但此篇主要分析上層MediaPlayer如何通過(guò)底層耍鬓,來(lái)遠(yuǎn)程關(guān)聯(lián)服務(wù)端的播放器,沒(méi)有涉及過(guò)多的Binder知識(shí)流妻,若熟悉此流程的讀者可以跳過(guò)牲蜀。
MediaPlayer通常有2種創(chuàng)建方式:
MediaPlayer mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource( "http://xxxx" );
mediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
mediaPlayer.start();
MediaPlayer mediaPlayer = MediaPlayer.create( context, R.raw.test);
mediaPlayer.start();
無(wú)論哪種方式,其調(diào)用執(zhí)行流程都是相同的绅这,我們以第一種為例開(kāi)始分析涣达,先看MediaPlayer 構(gòu)造方法。
public class MediaPlayer implements SubtitleController.Listener
{
static {
System.loadLibrary("media_jni");
native_init();
}
public MediaPlayer() {
//構(gòu)建AudioAttributes
super((new Builder()).build(), 2);
Looper looper;
//myLooper不為null证薇,構(gòu)建EventHandler
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
//獲取主線程Looper不為null度苔,構(gòu)建EventHandler
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
this.mTimeProvider = new MediaPlayer.TimeProvider(this);
this.mOpenSubtitleSources = new Vector();
//構(gòu)建弱引用,執(zhí)行native_setup
this.native_setup(new WeakReference(this));
this.baseRegisterPlayer();
}
EventHandler用于處理底層回調(diào)過(guò)來(lái)的狀態(tài)棕叫,用來(lái)回調(diào)用戶設(shè)置的監(jiān)聽(tīng)器林螃。下面再分析。我們注意到靜態(tài)代碼塊中加載了libmedia_jni.so俺泣,并調(diào)用native_init進(jìn)行了初始化疗认,它在android_media_MediaPlayer.cpp中完残。
static void
android_media_MediaPlayer_native_init(JNIEnv *env)
{
jclass clazz;
//獲取Java的MediaPlayer類
clazz = env->FindClass("android/media/MediaPlayer");
if (clazz == NULL) {
return;
}
//獲取mNativeContext變量id,保存到fields.context
fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.context == NULL) {
return;
}
//獲取postEventFromNative方法id横漏,保存到fields.post_event
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.post_event == NULL) {
return;
}
fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
if (fields.surface_texture == NULL) {
return;
}
//獲取ProxyInfo類
clazz = env->FindClass("android/net/ProxyInfo");
if (clazz == NULL) {
return;
}
fields.proxyConfigGetHost =
env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");
fields.proxyConfigGetPort =
env->GetMethodID(clazz, "getPort", "()I");
fields.proxyConfigGetExclusionList =
env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");
}
先看fields是什么谨设。
struct fields_t {
jfieldID context;
jfieldID surface_texture;
jmethodID post_event;
jmethodID proxyConfigGetHost;
jmethodID proxyConfigGetPort;
jmethodID proxyConfigGetExclusionList;
};
static fields_t fields;
初始化的工作就是將MediaPlayer.java中定義的一些字段或方法的ID保存到fields_t 這個(gè)結(jié)構(gòu)體中。這是一種通過(guò)本地去初始化Java層的類成員的方法缎浇,比如mNativeContext字段扎拣,隨后底層將構(gòu)造一個(gè)本地MediaPlayer對(duì)象,將這個(gè)對(duì)象的地址保存到mNativeContext變量中素跺,也就是說(shuō)二蓝,每創(chuàng)建一個(gè)Java類的MediaPlayer對(duì)象,也將綁定了一個(gè)Native層的MediaPlayer對(duì)象指厌。
因此刊愚,從Native角度來(lái)說(shuō),我們平時(shí)使用的MediaPlayer.java像是底層暴露的一個(gè)接口類踩验,實(shí)際的實(shí)現(xiàn)邏輯都是由底層處理的鸥诽。由于Java層、Jni層和C++層用的名字都是MediaPlayer箕憾,為了描述方便牡借,后面將用MediaPlayer.java、MediaPlayer.jni和MediaPlayer.cpp來(lái)做區(qū)分袭异。
我們?cè)倩仡^看MediaPlayer.java構(gòu)造方法中钠龙,被調(diào)用的native_setup方法,它將當(dāng)前對(duì)象以及對(duì)象弱引用傳遞給底層御铃。
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
//構(gòu)件MediaPlayer.cpp對(duì)象
sp<MediaPlayer> mp = new MediaPlayer();
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
// 構(gòu)建JNIMediaPlayerListener對(duì)象俊鱼,傳入了Java層對(duì)象引用和弱引用
sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
//保存到MediaPlayer.cpp對(duì)象中
mp->setListener(listener);
//將MediaPlayer.cpp對(duì)象保存到MediaPlayer.java的mNativeContext字段中
setMediaPlayer(env, thiz, mp);
}
先來(lái)看MediaPlayer.cpp對(duì)象的構(gòu)建。
MediaPlayer::MediaPlayer()
{
ALOGV("constructor");
mListener = NULL;
mCookie = NULL;
mStreamType = AUDIO_STREAM_MUSIC;//默認(rèn)音頻流類型
mAudioAttributesParcel = NULL;
mCurrentPosition = -1;
mSeekPosition = -1;
mCurrentState = MEDIA_PLAYER_IDLE;//初始狀態(tài)為idle空閑狀態(tài)
mPrepareSync = false;
mPrepareStatus = NO_ERROR;
mLoop = false; //不循環(huán)播放
mLeftVolume = mRightVolume = 1.0;
mVideoWidth = mVideoHeight = 0;
mLockThreadId = 0;
mAudioSessionId = AudioSystem::newAudioUniqueId();
AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
mSendLevel = 0;
mRetransmitEndpointValid = false;
}
MediaPlayer.cpp構(gòu)造函數(shù)只是對(duì)一些字段做了初始化畅买,如設(shè)置默認(rèn)的流類型為音頻流,初始狀態(tài)為空閑狀態(tài)细睡。
JNIMediaPlayerListener聲明了一個(gè)notify()函數(shù)谷羞,用于底層產(chǎn)生錯(cuò)誤時(shí),通知MediaPlayer.java拋出異常的溜徙。
class JNIMediaPlayerListener: public MediaPlayerListener
{
public:
JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
~JNIMediaPlayerListener();
virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
private:
JNIMediaPlayerListener();
jclass mClass; //MediaPlayer.java對(duì)象引用的全局引用
jobject mObject; //MediaPlayer.java對(duì)象弱引用的全局引用
};
JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
{
// 獲取MediaPlayer.java
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
ALOGE("Can't find android/media/MediaPlayer");
jniThrowException(env, "java/lang/Exception", NULL);
return;
}
//創(chuàng)建對(duì)象引用的全局引用
mClass = (jclass)env->NewGlobalRef(clazz);
//創(chuàng)建對(duì)象弱引用的全局引用
mObject = env->NewGlobalRef(weak_thiz);
}
JNIMediaPlayerListener::~JNIMediaPlayerListener()
{
//釋放引用
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mObject);
env->DeleteGlobalRef(mClass);
}
void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (obj && obj->dataSize() > 0) {
jobject jParcel = createJavaParcelObject(env);
if (jParcel != NULL) {
Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
nativeParcel->setData(obj->data(), obj->dataSize());
//調(diào)用MediaPlayer.java對(duì)象的postEventFromNative靜態(tài)方法
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, jParcel);
env->DeleteLocalRef(jParcel);
}
} else {
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, NULL);
}
if (env->ExceptionCheck()) {
ALOGW("An exception occurred while notifying an event.");
LOGW_EX(env);
env->ExceptionClear();
}
}
JNIMediaPlayerListener為對(duì)象的引用和弱引用創(chuàng)建全局引用并保存起來(lái)湃缎。如果notify函數(shù)被調(diào)用了,將回調(diào)MediaPlayer.java的postEventFromNative方法蠢壹。
private static void postEventFromNative(Object mediaplayer_ref,
int what, int arg1, int arg2, Object obj)
{
//獲取弱引用
MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();
if (mp == null) {
return;
}
if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
mp.start();
}
//發(fā)送消息給EventHandler
if (mp.mEventHandler != null) {
Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
mp.mEventHandler.sendMessage(m);
}
}
EventHandler處理的狀態(tài)比較多嗓违,我們只貼幾個(gè)常見(jiàn)的。
private class EventHandler extends Handler
{
private MediaPlayer mMediaPlayer;
public EventHandler(MediaPlayer mp, Looper looper) {
super(looper);
mMediaPlayer = mp;
}
@Override
public void handleMessage(Message msg) {
//MediaPlayer.cpp為null
if (mMediaPlayer.mNativeContext == 0) {
return;
}
//準(zhǔn)備成功回調(diào)
switch(msg.what) {
case MEDIA_PREPARED:
scanInternalSubtitleTracks();
if (mOnPreparedListener != null)
mOnPreparedListener.onPrepared(mMediaPlayer);
return;
//播放完成回調(diào)
case MEDIA_PLAYBACK_COMPLETE:
if (mOnCompletionListener != null)
mOnCompletionListener.onCompletion(mMediaPlayer);
stayAwake(false);
return;
//停止回調(diào)
case MEDIA_STOPPED:
if (mTimeProvider != null) {
mTimeProvider.onStopped();
}
break;
//暫屯济常回調(diào)
case MEDIA_STARTED:
case MEDIA_PAUSED:
if (mTimeProvider != null) {
mTimeProvider.onPaused(msg.what == MEDIA_PAUSED);
}
break;
......
在MediaPlayer.jni的native_setup函數(shù)中蹂季,JNIMediaPlayerListener對(duì)象被構(gòu)建后冕广,會(huì)先保存在MediaPlayer.cpp對(duì)象中,緊接著執(zhí)行setMediaPlayer函數(shù)偿洁。
static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
Mutex::Autolock l(sLock);
//獲取舊的
sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
if (player.get()) {
player->incStrong((void*)setMediaPlayer);
}
//將舊的釋放
if (old != 0) {
old->decStrong((void*)setMediaPlayer);
}
//給fields.context設(shè)置新的MediaPlayer.cpp
env->SetLongField(thiz, fields.context, (jlong)player.get());
return old;
}
到這里撒汉,Java層的MediaPlayer.java對(duì)象便通過(guò)mNativeContext變量,綁定了底層的MediaPlayer.cpp對(duì)象涕滋。
接著看setDataSource方法睬辐。
public void setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException {
_setDataSource(fd, offset, length);
}
private native void _setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException;
_setDataSource的綁定使用動(dòng)態(tài)注冊(cè)方式實(shí)現(xiàn)。
static JNINativeMethod gMethods[] = {
{
"nativeSetDataSource",
"(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
"[Ljava/lang/String;)V",
(void *)android_media_MediaPlayer_setDataSourceAndHeaders
},
//_setDataSource映射到android_media_MediaPlayer_setDataSourceFD
{"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface},
{"_prepare", "()V", (void *)android_media_MediaPlayer_prepare},
{"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
{"_start", "()V", (void *)android_media_MediaPlayer_start},
{"_stop", "()V", (void *)android_media_MediaPlayer_stop},
因此宾肺,我們看android_media_MediaPlayer_setDataSourceFD這個(gè)函數(shù)溯饵。
static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
//獲取MediaPlayer.cpp對(duì)象
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
if (fileDescriptor == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
ALOGV("setDataSourceFD: fd %d", fd);
//調(diào)用MediaPlayer.cpp對(duì)象的setDataSource函數(shù)
process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}
//獲取MediaPlayer.java對(duì)象里綁定的MediaPlayer.cpp對(duì)象
static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
{
Mutex::Autolock l(sLock);
MediaPlayer* const p = (MediaPlayer*)env->GetLongField(thiz, fields.context);
return sp<MediaPlayer>(p);
}
調(diào)用MediaPlayer.cpp對(duì)象如果出現(xiàn)錯(cuò)誤或異常,都會(huì)通過(guò)process_media_player_call來(lái)通知上層的MediaPlayer.java對(duì)象锨用,如何通知在上面已經(jīng)分析過(guò)丰刊,它會(huì)調(diào)用MediaPlayer.cpp對(duì)象的成員JNIMediaPlayerListener的notify方法。
static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
if (exception == NULL) {
if (opStatus != (status_t) OK) {
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
}
} else {
if ( opStatus == (status_t) INVALID_OPERATION ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
} else if ( opStatus == (status_t) PERMISSION_DENIED ) {
jniThrowException(env, "java/lang/SecurityException", NULL);
} else if ( opStatus != (status_t) OK ) {
if (strlen(message) > 230) {
jniThrowException( env, exception, message);
} else {
char msg[256];
sprintf(msg, "%s: status=0x%X", message, opStatus);
jniThrowException( env, exception, msg);
}
}
}
}
調(diào)用process_media_player_call函數(shù)時(shí)黔酥,調(diào)用了MediaPlayer.cpp對(duì)象的setDataSource函數(shù)藻三。
status_t MediaPlayer::setDataSource(
const sp<IMediaHTTPService> &httpService,
const char *url, const KeyedVector<String8, String8> *headers)
{
ALOGV("setDataSource(%s)", url);
status_t err = BAD_VALUE;
if (url != NULL) {
//通過(guò)ServiceManager找到MediaPlayerService,初始化service對(duì)象
const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
//通過(guò)Binder機(jī)制向MediaPlayerService請(qǐng)求創(chuàng)建IMediaPlayer對(duì)象
sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
//調(diào)用IMediaPlayer的setDataSource
(NO_ERROR != player->setDataSource(httpService, url, headers))) {
player.clear();
}
//關(guān)聯(lián)遠(yuǎn)程播放器
err = attachNewPlayer(player);
}
}
return err;
}
getMediaPlayerService便是通過(guò)Binder機(jī)制跪者,從ServiceManger中獲取到一個(gè)MediaPlayerService棵帽,并創(chuàng)建創(chuàng)建IMediaPlayer對(duì)象,這個(gè)函數(shù)在IMediaDeathNotifier.cpp中實(shí)現(xiàn)渣玲。
IMediaDeathNotifier::getMediaPlayerService()
{
ALOGV("getMediaPlayerService");
Mutex::Autolock _l(sServiceLock);
if (sMediaPlayerService == 0) {
//獲取ServiceManger
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
//獲取binder
binder = sm->getService(String16("media.player"));
if (binder != 0) {
break;
}
ALOGW("Media player service not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(sDeathNotifier);
//將binder強(qiáng)轉(zhuǎn)為IMediaPlayerService
sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
}
ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
//返回IMediaPlayerService
return sMediaPlayerService;
}
從ServiceManger中獲取到Binder并將其強(qiáng)轉(zhuǎn)成IMediaPlayerService類型逗概。在MediaServer創(chuàng)建時(shí),MediaPlayerService便注冊(cè)在MediaServer中了忘衍,注冊(cè)過(guò)程我們將在下一篇分析逾苫。獲取到IMediaPlayerService后,緊接著調(diào)用create函數(shù)創(chuàng)建IMediaPlayer對(duì)象枚钓。
MediaPlayer.cpp實(shí)際上是IMediaPlayerClient類型的铅搓,即C端,顯然IMediaPlayerService就是S端了搀捷。
sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
int audioSessionId)
{
//每個(gè)線程只有一個(gè)IPCThreadState對(duì)象星掰,它保存了進(jìn)程ProcessState對(duì)象,負(fù)責(zé)binder的讀取嫩舟、寫(xiě)入和請(qǐng)求
pid_t pid = IPCThreadState::self()->getCallingPid();
int32_t connId = android_atomic_inc(&mNextConnId);
//創(chuàng)建Client
sp<Client> c = new Client(
this, pid, connId, client, audioSessionId,
IPCThreadState::self()->getCallingUid());
ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,
IPCThreadState::self()->getCallingUid());
//轉(zhuǎn)弱引用
wp<Client> w = c;
{
Mutex::Autolock lock(mLock);
//保存到SortedVector中
mClients.add(w);
}
return c;
}
這個(gè)Client是MediaPlayerService的內(nèi)部類氢烘,而且它被添加到MediaPlayerService的全局列表mClients中,這也說(shuō)明了上層MediaPlayer.java可以同時(shí)有多個(gè)家厌。
private:
class Client : public BnMediaPlayer {
// IMediaPlayer interface
virtual void disconnect();
virtual status_t setVideoSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer);
virtual status_t prepareAsync();
virtual status_t start();
virtual status_t stop();
virtual status_t pause();
virtual status_t isPlaying(bool* state);
virtual status_t seekTo(int msec);
virtual status_t getCurrentPosition(int* msec);
virtual status_t getDuration(int* msec);
virtual status_t reset();
virtual status_t setAudioStreamType(audio_stream_type_t type);
virtual status_t setLooping(int loop);
virtual status_t setVolume(float leftVolume, float rightVolume);
virtual status_t invoke(const Parcel& request, Parcel *reply);
virtual status_t setMetadataFilter(const Parcel& filter);
virtual status_t getMetadata(bool update_only,
bool apply_filter,
Parcel *reply);
virtual status_t setAuxEffectSendLevel(float level);
virtual status_t attachAuxEffect(int effectId);
virtual status_t setParameter(int key, const Parcel &request);
virtual status_t getParameter(int key, Parcel *reply);
virtual status_t setRetransmitEndpoint(const struct sockaddr_in* endpoint);
virtual status_t getRetransmitEndpoint(struct sockaddr_in* endpoint);
virtual status_t setNextPlayer(const sp<IMediaPlayer>& player);
我們看到播玖,這個(gè)Client的函數(shù)定義和MediaPlayer.cpp和MediaPlayer.java是一一對(duì)應(yīng)的,它的繼承關(guān)系為Client->BnMediaPlayer->IMediaPlayer饭于,也就是說(shuō)蜀踏,Client本質(zhì)是一個(gè)IMediaPlayer维蒙,它可以創(chuàng)建和控制播放器。Binder通訊中會(huì)經(jīng)常見(jiàn)到Bnxxx和Bpxxx脓斩,它們的區(qū)別以及繼承關(guān)系會(huì)在下一篇分析木西。
再回到MediaPlayer::setDataSource函數(shù),可以看到實(shí)際將調(diào)用IMediaPlayer的setDataSource函數(shù)随静。
status_t MediaPlayerService::Client::setDataSource(
const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers)
{
//路徑不能為null
ALOGV("setDataSource(%s)", url);
if (url == NULL)
return UNKNOWN_ERROR;
//路徑類型
if ((strncmp(url, "http://", 7) == 0) ||
(strncmp(url, "https://", 8) == 0) ||
(strncmp(url, "rtsp://", 7) == 0)) {
if (!checkPermission("android.permission.INTERNET")) {
return PERMISSION_DENIED;
}
}
//是否內(nèi)容提供者提供數(shù)據(jù)
if (strncmp(url, "content://", 10) == 0) {
String16 url16(url);
int fd = android::openContentProviderFile(url16);
if (fd < 0)
{
ALOGE("Couldn't open fd for %s", url);
return UNKNOWN_ERROR;
}
setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatus
close(fd);
return mStatus;
} else {
//根據(jù)url獲取播放器類型
player_type playerType = MediaPlayerFactory::getPlayerType(this, url);
//創(chuàng)建對(duì)應(yīng)的播放器
sp<MediaPlayerBase> p = setDataSource_pre(playerType);
if (p == NULL) {
return NO_INIT;
}
//對(duì)調(diào)用播放器的setDataSource
setDataSource_post(p, p->setDataSource(httpService, url, headers));
return mStatus;
}
}
安卓的播放器實(shí)際有兩種八千,StagefrightPlayer和NuPlayer,前者是對(duì)AwesomePlayer的封裝燎猛。這里的創(chuàng)建使用抽象工廠模式恋捆,播放器對(duì)應(yīng)的工廠對(duì)象,在MediaPlayerService構(gòu)造函數(shù)中初始化重绷,維護(hù)在MediaPlayerService的成員Map中沸停。
遠(yuǎn)程服務(wù)端的播放器通過(guò)資源路徑創(chuàng)建完畢,MediaPlayer.cpp將通過(guò)關(guān)聯(lián)遠(yuǎn)程Client(IMediaPlayer)來(lái)關(guān)聯(lián)這個(gè)播放器昭卓,通過(guò)調(diào)用MediaPlayer.cpp的attachNewPlayer實(shí)現(xiàn)愤钾。
status_t MediaPlayer::attachNewPlayer(const sp<IMediaPlayer>& player)
{
status_t err = UNKNOWN_ERROR;
sp<IMediaPlayer> p;
{
//加鎖
Mutex::Autolock _l(mLock);
//出錯(cuò)
if ( !( (mCurrentState & MEDIA_PLAYER_IDLE) ||
(mCurrentState == MEDIA_PLAYER_STATE_ERROR ) ) ) {
ALOGE("attachNewPlayer called in state %d", mCurrentState);
return INVALID_OPERATION;
}
// Client(IMediaPlayer)保存到成員變量mPlayer 中
clear_l();
p = mPlayer;
mPlayer = player;
//狀態(tài)置為MEDIA_PLAYER_INITIALIZED
if (player != 0) {
mCurrentState = MEDIA_PLAYER_INITIALIZED;
err = NO_ERROR;
} else {
ALOGE("Unable to create media player");
}
}
if (p != 0) {
p->disconnect();
}
return err;
}
到此,上層應(yīng)用便和底層的播放器進(jìn)行了綁定候醒,MediaPlayer.java調(diào)用prepare和start等方法能颁,都將通過(guò)底層的播放器去執(zhí)行具體的邏輯。
這一篇只分析了播放器的創(chuàng)建和關(guān)聯(lián)流程倒淫,其中還留下了許多疑問(wèn)伙菊,并且Binder的遠(yuǎn)程調(diào)用具體過(guò)程,也還沒(méi)有分析到敌土,這些都將再下一篇進(jìn)行講解镜硕。
最后,從底層的角度出發(fā)返干,對(duì)各個(gè)類以及各層之間的關(guān)系做下總結(jié):
MediaPlayerService是服務(wù)端兴枯,它主要功能是創(chuàng)建Client對(duì)象并維護(hù)了一個(gè)Client列表,以供客戶端調(diào)用矩欠。
Client實(shí)現(xiàn)了IMediaPlayer定義的子類念恍,它相當(dāng)于播放器對(duì)外暴露的接口,操作Client就能操作播放器晚顷。
MediaPlayer.cpp是相對(duì)于MediaPlayerService的客戶端,同時(shí)它相當(dāng)于暴露給MediaPlayer.jni的接口疗疟,它通過(guò)Binder機(jī)制该默,遠(yuǎn)程調(diào)用運(yùn)行在MediaPlayerService里的Client(IMediaPlayer),來(lái)操作播放器策彤。
同樣栓袖,MediaPlayer.jni相當(dāng)于底層暴露給應(yīng)用層MediaPlayer.java的接口匣摘。