Binder(一)

《Binder簡介》一篇中,我們了解了Binder進程間通訊的大致執行原理,從這一篇開始,通過分析源碼來認識Binder底層的調用過程。

Binder結構

Binder采用C/S結構的通訊方式,提供服務的為Server進程,訪問的進程為Client進程,一個Server進程可以運行多個組件來為Client服務,這些組件稱為Service組件。Client和Server進程都維護了自己的一個Binder線程池,因此,Client可以并發訪問,Server也可以并發提供服務。每個Service組件在啟動時,都會添加進ServiceManager中,它用來對組件進行統一管理。Client和Service組件由應用程序實現。ServiceManager和Binder驅動由系統實現。

/dev/binder是Binder驅動對外暴露的設備文件,Client和Server進程,通過系統調用open、mmap和ioctl來訪問/dev/binder文件,進而訪問Binder驅動。我們知道,在Linux中,通過操作文件的方式,便能間接控制設備,因此,操作/dev/binder文件,就相當于在操作Binder驅動了。

image.png

MediaPlayer

之所以選擇MediaPlayer作為理解Binder的切入點,原因有二:一是媒體播放這種需求比較常見;二是,MediaPlayer (Client)需要通過底層MediaServer(Server)管理運行的組件MediaPlayerService,來實現上層播放功能,而MediaServer包含了許多重要的Service組件,如:

  • AudioFlinger:音頻系統中的核心服務。
  • AudioPolicyService:音頻系統中關于音頻策略的重要任務。
  • CameraService:攝像/照相的重要服務。
  • MediaPlayerService:多媒體系統中的重要服務。

但此篇主要分析上層MediaPlayer如何通過底層,來遠程關聯服務端的播放器,沒有涉及過多的Binder知識,若熟悉此流程的讀者可以跳過。

MediaPlayer通常有2種創建方式:

  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();

無論哪種方式,其調用執行流程都是相同的,我們以第一種為例開始分析,先看MediaPlayer 構造方法。

public class MediaPlayer implements SubtitleController.Listener
{

  static {
        System.loadLibrary("media_jni");
        native_init();
    }

 public MediaPlayer() {
      //構建AudioAttributes
       super((new Builder()).build(), 2);

        Looper looper;
        //myLooper不為null,構建EventHandler
        if ((looper = Looper.myLooper()) != null) {
            mEventHandler = new EventHandler(this, looper);
        //獲取主線程Looper不為null,構建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();
        //構建弱引用,執行native_setup
        this.native_setup(new WeakReference(this));
        this.baseRegisterPlayer();
    }

EventHandler用于處理底層回調過來的狀態,用來回調用戶設置的監聽器。下面再分析。我們注意到靜態代碼塊中加載了libmedia_jni.so,并調用native_init進行了初始化,它在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 這個結構體中。這是一種通過本地去初始化Java層的類成員的方法,比如mNativeContext字段,隨后底層將構造一個本地MediaPlayer對象,將這個對象的地址保存到mNativeContext變量中,也就是說,每創建一個Java類的MediaPlayer對象,也將綁定了一個Native層的MediaPlayer對象。

因此,從Native角度來說,我們平時使用的MediaPlayer.java像是底層暴露的一個接口類,實際的實現邏輯都是由底層處理的。由于Java層、Jni層和C++層用的名字都是MediaPlayer,為了描述方便,后面將用MediaPlayer.java、MediaPlayer.jni和MediaPlayer.cpp來做區分。

我們再回頭看MediaPlayer.java構造方法中,被調用的native_setup方法,它將當前對象以及對象弱引用傳遞給底層。

static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
   //構件MediaPlayer.cpp對象
    sp<MediaPlayer> mp = new MediaPlayer();
    if (mp == NULL) {
        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
        return;
    }

    // 構建JNIMediaPlayerListener對象,傳入了Java層對象引用和弱引用
    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
    //保存到MediaPlayer.cpp對象中
    mp->setListener(listener);

    //將MediaPlayer.cpp對象保存到MediaPlayer.java的mNativeContext字段中
    setMediaPlayer(env, thiz, mp);
}

先來看MediaPlayer.cpp對象的構建。

MediaPlayer::MediaPlayer()
{
    ALOGV("constructor");
    mListener = NULL;
    mCookie = NULL;
    mStreamType = AUDIO_STREAM_MUSIC;//默認音頻流類型
    mAudioAttributesParcel = NULL;
    mCurrentPosition = -1;
    mSeekPosition = -1;
    mCurrentState = MEDIA_PLAYER_IDLE;//初始狀態為idle空閑狀態
    mPrepareSync = false;
    mPrepareStatus = NO_ERROR;
    mLoop = false;  //不循環播放
    mLeftVolume = mRightVolume = 1.0;
    mVideoWidth = mVideoHeight = 0;
    mLockThreadId = 0;
    mAudioSessionId = AudioSystem::newAudioUniqueId();
    AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
    mSendLevel = 0;
    mRetransmitEndpointValid = false;
}

MediaPlayer.cpp構造函數只是對一些字段做了初始化,如設置默認的流類型為音頻流,初始狀態為空閑狀態。

JNIMediaPlayerListener聲明了一個notify()函數,用于底層產生錯誤時,通知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對象引用的全局引用
    jobject     mObject;    //MediaPlayer.java對象弱引用的全局引用
};

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;
    }
    //創建對象引用的全局引用
    mClass = (jclass)env->NewGlobalRef(clazz);
    //創建對象弱引用的全局引用
    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());

            //調用MediaPlayer.java對象的postEventFromNative靜態方法
            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為對象的引用和弱引用創建全局引用并保存起來。如果notify函數被調用了,將回調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();
        }
        //發送消息給EventHandler
        if (mp.mEventHandler != null) {
            Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
            mp.mEventHandler.sendMessage(m);
        }
    }

EventHandler處理的狀態比較多,我們只貼幾個常見的。

  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;
            }
            //準備成功回調
            switch(msg.what) {
            case MEDIA_PREPARED:
                scanInternalSubtitleTracks();
                if (mOnPreparedListener != null)
                    mOnPreparedListener.onPrepared(mMediaPlayer);
                return;
          //播放完成回調
            case MEDIA_PLAYBACK_COMPLETE:
                if (mOnCompletionListener != null)
                    mOnCompletionListener.onCompletion(mMediaPlayer);
                stayAwake(false);
                return;
          //停止回調
            case MEDIA_STOPPED:
                if (mTimeProvider != null) {
                    mTimeProvider.onStopped();
                }
                break;
           //暫?;卣{
            case MEDIA_STARTED:
            case MEDIA_PAUSED:
                if (mTimeProvider != null) {
                    mTimeProvider.onPaused(msg.what == MEDIA_PAUSED);
                }
                break;
        ......

在MediaPlayer.jni的native_setup函數中,JNIMediaPlayerListener對象被構建后,會先保存在MediaPlayer.cpp對象中,緊接著執行setMediaPlayer函數。

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設置新的MediaPlayer.cpp
    env->SetLongField(thiz, fields.context, (jlong)player.get());
    return old;
}

到這里,Java層的MediaPlayer.java對象便通過mNativeContext變量,綁定了底層的MediaPlayer.cpp對象。

接著看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的綁定使用動態注冊方式實現。

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這個函數。

static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
    //獲取MediaPlayer.cpp對象
    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);
  
    //調用MediaPlayer.cpp對象的setDataSource函數   
    process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}

//獲取MediaPlayer.java對象里綁定的MediaPlayer.cpp對象
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);
}

調用MediaPlayer.cpp對象如果出現錯誤或異常,都會通過process_media_player_call來通知上層的MediaPlayer.java對象,如何通知在上面已經分析過,它會調用MediaPlayer.cpp對象的成員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);
            }
        }
    }
}

調用process_media_player_call函數時,調用了MediaPlayer.cpp對象的setDataSource函數。

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) {
        //通過ServiceManager找到MediaPlayerService,初始化service對象
        const sp<IMediaPlayerService>& service(getMediaPlayerService());
        if (service != 0) {
            //通過Binder機制向MediaPlayerService請求創建IMediaPlayer對象
            sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
            if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
                //調用IMediaPlayer的setDataSource
                (NO_ERROR != player->setDataSource(httpService, url, headers))) {
                player.clear();
            }
            //關聯遠程播放器
            err = attachNewPlayer(player);
        }
    }
    return err;
}

getMediaPlayerService便是通過Binder機制,從ServiceManger中獲取到一個MediaPlayerService,并創建創建IMediaPlayer對象,這個函數在IMediaDeathNotifier.cpp中實現。

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強轉為IMediaPlayerService
        sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
    }
    ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
    //返回IMediaPlayerService
    return sMediaPlayerService;
}

從ServiceManger中獲取到Binder并將其強轉成IMediaPlayerService類型。在MediaServer創建時,MediaPlayerService便注冊在MediaServer中了,注冊過程我們將在下一篇分析。獲取到IMediaPlayerService后,緊接著調用create函數創建IMediaPlayer對象。

MediaPlayer.cpp實際上是IMediaPlayerClient類型的,即C端,顯然IMediaPlayerService就是S端了。

sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
        int audioSessionId)
{
   //每個線程只有一個IPCThreadState對象,它保存了進程ProcessState對象,負責binder的讀取、寫入和請求
    pid_t pid = IPCThreadState::self()->getCallingPid();
    int32_t connId = android_atomic_inc(&mNextConnId);
    //創建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());
    //轉弱引用
    wp<Client> w = c;
    {
        Mutex::Autolock lock(mLock);
      //保存到SortedVector中
        mClients.add(w);
    }
    return c;
}

這個Client是MediaPlayerService的內部類,而且它被添加到MediaPlayerService的全局列表mClients中,這也說明了上層MediaPlayer.java可以同時有多個。

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);

我們看到,這個Client的函數定義和MediaPlayer.cpp和MediaPlayer.java是一一對應的,它的繼承關系為Client->BnMediaPlayer->IMediaPlayer,也就是說,Client本質是一個IMediaPlayer,它可以創建和控制播放器。Binder通訊中會經常見到Bnxxx和Bpxxx,它們的區別以及繼承關系會在下一篇分析。

再回到MediaPlayer::setDataSource函數,可以看到實際將調用IMediaPlayer的setDataSource函數。

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;
        }
    }

      //是否內容提供者提供數據
    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 {
        //根據url獲取播放器類型
        player_type playerType = MediaPlayerFactory::getPlayerType(this, url);
        //創建對應的播放器
        sp<MediaPlayerBase> p = setDataSource_pre(playerType);
        if (p == NULL) {
            return NO_INIT;
        }
        //對調用播放器的setDataSource
        setDataSource_post(p, p->setDataSource(httpService, url, headers));
        return mStatus;
    }
}

安卓的播放器實際有兩種,StagefrightPlayer和NuPlayer,前者是對AwesomePlayer的封裝。這里的創建使用抽象工廠模式,播放器對應的工廠對象,在MediaPlayerService構造函數中初始化,維護在MediaPlayerService的成員Map中。

遠程服務端的播放器通過資源路徑創建完畢,MediaPlayer.cpp將通過關聯遠程Client(IMediaPlayer)來關聯這個播放器,通過調用MediaPlayer.cpp的attachNewPlayer實現。

status_t MediaPlayer::attachNewPlayer(const sp<IMediaPlayer>& player)
{
    status_t err = UNKNOWN_ERROR;
    sp<IMediaPlayer> p;
    { 
      //加鎖
        Mutex::Autolock _l(mLock);
        //出錯
        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;
        //狀態置為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;
}

到此,上層應用便和底層的播放器進行了綁定,MediaPlayer.java調用prepare和start等方法,都將通過底層的播放器去執行具體的邏輯。

這一篇只分析了播放器的創建和關聯流程,其中還留下了許多疑問,并且Binder的遠程調用具體過程,也還沒有分析到,這些都將再下一篇進行講解。

最后,從底層的角度出發,對各個類以及各層之間的關系做下總結:

  • MediaPlayerService是服務端,它主要功能是創建Client對象并維護了一個Client列表,以供客戶端調用。

  • Client實現了IMediaPlayer定義的子類,它相當于播放器對外暴露的接口,操作Client就能操作播放器。

  • MediaPlayer.cpp是相對于MediaPlayerService的客戶端,同時它相當于暴露給MediaPlayer.jni的接口,它通過Binder機制,遠程調用運行在MediaPlayerService里的Client(IMediaPlayer),來操作播放器。

  • 同樣,MediaPlayer.jni相當于底層暴露給應用層MediaPlayer.java的接口。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,362評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,013評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,346評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,421評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,146評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,534評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,585評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,767評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,318評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,074評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,258評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,828評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,486評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,916評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,156評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,993評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,234評論 2 375

推薦閱讀更多精彩內容