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阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容