Binder(一)

《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)了。

image.png

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的接口匣摘。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市裹刮,隨后出現(xiàn)的幾起案子音榜,更是在濱河造成了極大的恐慌,老刑警劉巖捧弃,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赠叼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡违霞,警方通過(guò)查閱死者的電腦和手機(jī)嘴办,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)买鸽,“玉大人涧郊,你說(shuō)我怎么就攤上這事⊙畚澹” “怎么了妆艘?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)看幼。 經(jīng)常有香客問(wèn)我批旺,道長(zhǎng),這世上最難降的妖魔是什么桌吃? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任朱沃,我火速辦了婚禮,結(jié)果婚禮上茅诱,老公的妹妹穿的比我還像新娘逗物。我一直安慰自己,他們只是感情好瑟俭,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布翎卓。 她就那樣靜靜地躺著,像睡著了一般摆寄。 火紅的嫁衣襯著肌膚如雪失暴。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天微饥,我揣著相機(jī)與錄音逗扒,去河邊找鬼。 笑死欠橘,一個(gè)胖子當(dāng)著我的面吹牛矩肩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肃续,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼黍檩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼叉袍!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起刽酱,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤喳逛,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后棵里,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體润文,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年衍慎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了转唉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡稳捆,死狀恐怖赠法,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情乔夯,我是刑警寧澤砖织,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站末荐,受9級(jí)特大地震影響侧纯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜甲脏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一眶熬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧块请,春花似錦娜氏、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至海渊,卻和暖如春绵疲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背臣疑。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工盔憨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讯沈。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓郁岩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子驯用,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容