基于android6.0 mediaplayer框架分析

来源:互联网 发布:数据与安全监察委员会 编辑:程序博客网 时间:2024/06/06 14:39

        Mediaplayer的构造函数里面定义了一个Looper,将该Looper初始化为当前线程使用的Looper,如果为空则初始化为主线程的Looper,再使用该Looper作为参数初始化EventHandler对象。mAppOps保存了AppOpsService的代理对象,AppOpsService功能是检测某些应用是否具有某些权限。

 public MediaPlayer() {        Looper looper;        if ((looper = Looper.myLooper()) != null) {            mEventHandler = new EventHandler(this, looper);        } else if ((looper = Looper.getMainLooper()) != null) {            mEventHandler = new EventHandler(this, looper);        } else {            mEventHandler = null;        }        mTimeProvider = new TimeProvider(this);        mOpenSubtitleSources = new Vector<InputStream>();        IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);        mAppOps = IAppOpsService.Stub.asInterface(b);        /* Native setup requires a weak reference to our object.         * It's easier to create it here than in C++.         */        native_setup(new WeakReference<MediaPlayer>(this));    }

         接下来调用native_setup的native方法进入native层。 Mediaplayer.java的静态代码块中加载了libmedia_jni.so库,这个操作回去调用JNI层的JNI_OnLoad函数。 JNI_Onload函数作用在与确认JNI的版本1.4,在JNI层注册个种组件。JNI_onload同样也注册了MediaPlayer。

jint JNI_OnLoad(JavaVM* vm, void* /* reserved */){    JNIEnv* env = NULL;    jint result = -1;    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {        ALOGE("ERROR: GetEnv failed\n");        goto bail;    }    assert(env != NULL);    ...     if (register_android_media_MediaPlayer(env) < 0) {        ALOGE("ERROR: MediaPlayer native registration failed\n");        goto bail;    }    ...


         以native_int和native_setup为例说明对应关系: 1.无参数(),返回值为Void(V)的Java层函数native_init在JNI层对应的函数名为android_media_MediaPlayer_native_init。 2.接收一个Object参数,返回值为Void(V)的Java层函数native_setup在JNI层对应的函数名为android_media_MediaPlayer_native_setup。 

static JNINativeMethod gMethods[] = {    ... {"native_init",         "()V",                              (void *)android_media_MediaPlayer_native_init},    {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer_native_setup},    ...


        Java层静态代码块使用了native_init,该函数的作用在JNI层将Java层的信息保存在field_t结构体变量中。JNIEnv是一个与线程相关的变量,不同线程的JNIEnv相互独立。由于是获取类的FieldID和MethodID,android_media_MediaPlayer_native_init调用FindClass获得Java层MediaPlayer的class。GetFileldID接收三个参数,第一个为Java层的类,第二个是Field的名称,第三个是Field的签名(J代表Java层的long)。GetStaticMethodID接收三个参数,第一个为java类,第二个是Method的名称,第三个是Method的签名((Ljava/lang/Object;IIILjava/lang/Object;)V 表示接收两个Object参数,返回值为void)。 

static voidandroid_media_MediaPlayer_native_init(JNIEnv *env){    jclass clazz;    clazz = env->FindClass("android/media/MediaPlayer");    if (clazz == NULL) {        return;    }    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");    if (fields.context == NULL) {        return;    }    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;    }    env->DeleteLocalRef(clazz);    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;");    env->DeleteLocalRef(clazz);    gPlaybackParamsFields.init(env);    gSyncParamsFields.init(env);}


        下面讲解MediaPlayer构造函数中的native_setup函数。native_setup函数有一个Object参数,目的是传递到C++层保存Java层的MediaPlayer地址。同样地,native_setup在JNI对应的函数为android_media_MediaPlayer_native_setup。首先,函数new了一个C++层的MediaPlayer和一个JNIMediaPlayerListener。JNIMediaPlayerListener继承自MediaPlayerListener,并实现了notify函数。在JNIMediaPlayerListener构造函数中,首先从jobject中获得jclass,因为访问静态field和method传入的参数都是jclass而不是jobject,获得的jclass保存在mClass里。mObject保存了从Java层传下来的的MediaPlayer引用。在notify函数中,有一个Parcel类型的参数。createJavaParcelObject创建Java层的Parcel对象,以该Parcel为对象获得native层的Parcel对象。将Parcel参数的信息填入到这个native层的Parcel中。然后回调Java层的方法postEventFromNative(该MethodID保存在fields.post_event中)。并将一系列参数传递给postEventFromNative。

static voidandroid_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this){    ALOGV("native_setup");    sp<MediaPlayer> mp = new MediaPlayer();    if (mp == NULL) {        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");        return;    }    // create new listener and give it to MediaPlayer    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);    mp->setListener(listener);    // Stow our new C++ MediaPlayer in an opaque field in the Java object.    setMediaPlayer(env, thiz, mp);}

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


        回到android_media_MediaPlayer_native_setup函数中。C++层的MediaPlayer将上述的JNIMediaPlayerListener保存在成员变量mListener中,因此C++层的MediaPlayer能回调notify方法。setMediaPlayer方法中,第三个参数传入的是C++层创建的MediaPlayer的引用,此外,Java层的mNativeContext保存的是C++层MediaPlayer的地址。old变量获取mNativeContext的值。然后为C++层MediaPlayer的强引用计数加1,若mNativeContext仍保留旧的MediaPlayer,则对旧的MediaPlayer强引用计数减1。最后,将新的MediaPlayer的地址值填回到Java层的mNativeContext中。

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


        总而言之,android_media_MediaPlayer_native_setup函数主要工作是:构建C++层的MediaPlayer,并为该MediaPlayer设置监听事件,将该MediaPlayer的地址保存到Java层的mNativeContext中。

        MediaPlayer继承自BnMediaPlayerClient,这是一个本地端。代理端在BpMediaPlayerClient中。两者都实现了IMediaPlayerClient的notify接口。BpMediaPlayerClient继承自BpInterface模板类,而BpInterface继承自它的模板参数和BpRefBase类,这里的模板参数是IMediaPlayerClient。BpInterface可以通过Remote()方法得到BpBinder的指针,从而使BpMediaPlayerClient既具有IMediaPlayerClient的业务功能,也具有Binder的IPC功能。

class BpMediaPlayerClient: public BpInterface<IMediaPlayerClient>{public:    BpMediaPlayerClient(const sp<IBinder>& impl)        : BpInterface<IMediaPlayerClient>(impl)    {    }    virtual void notify(int msg, int ext1, int ext2, const Parcel *obj)    {        Parcel data, reply;        data.writeInterfaceToken(IMediaPlayerClient::getInterfaceDescriptor());        data.writeInt32(msg);        data.writeInt32(ext1);        data.writeInt32(ext2);        if (obj && obj->dataSize() > 0) {            data.appendFrom(const_cast<Parcel *>(obj), 0, obj->dataSize());        }        remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);    }};

class MediaPlayer : public BnMediaPlayerClient,                    public virtual IMediaDeathNotifier


        IMPLEMENT_META_INTERFACE(MediaPlayerClient,android.media.IMediaPlayerClient)展开后可得到相关的变量和函数实现。展开后可得到名字为android.media.IMediaPlayerClient的描述符,获得描述符的getInterfaceDescriptor方法和将BBBinder转化为BpMediaPlayerClient的方法asInterface。BnInterface继承自BBbinder,BBbinder继承自Ibinder,若在本地端调用(obj为BBbinder),调用的是BnInterface的queryLocalInterface方法,返回BnInterface的指针,再强转成IMediaPlayerClient的指针(BnInterface模板类继承自模板)。若在代理端调用(obj为BpBinder),intr为null,则以BpBinder为参数构建一个BpMediaPlayerClient对象。BpMediaPlayerClient父类BpRefBase的mRemote保存了BpBinder对象,因此具备IPC功能。 代理端通过notify方法与本地端进行通信。C++层的本地端读取数据后,修改配置参数,执行一些动作。然后通过JNIMediaPlayerListener的notify函数调回到Java端,执行Java端的postEventFromNative方法。第一件事就是通过第一个参数先找回之前Java层通过native_setup传到C++层的MediaPlayer对象。之后处理Message的过程不再详述。

 

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \    const android::String16 I##INTERFACE::descriptor(NAME);             \    const android::String16&                                            \            I##INTERFACE::getInterfaceDescriptor() const {              \        return I##INTERFACE::descriptor;                                \    }                                                                   \    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \            const android::sp<android::IBinder>& obj)                   \    {                                                                   \        android::sp<I##INTERFACE> intr;                                 \        if (obj != NULL) {                                              \            intr = static_cast<I##INTERFACE*>(                          \                obj->queryLocalInterface(                               \                        I##INTERFACE::descriptor).get());               \            if (intr == NULL) {                                         \                intr = new Bp##INTERFACE(obj);                          \            }                                                           \        }                                                               \        return intr;                                                    \    }                                                                   \    I##INTERFACE::I##INTERFACE() { }                                    \    I##INTERFACE::~I##INTERFACE() { }     

void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj){    ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);    bool send = true;    bool locked = false;    ...if ((listener != 0) && send) {        Mutex::Autolock _l(mNotifyLock);        ALOGV("callback application");        listener->notify(msg, ext1, ext2, obj);        ALOGV("back from callback");    }}

看看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) {            // this acquires the wakelock if needed, and sets the client side state            mp.start();        }        if (mp.mEventHandler != null) {            Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);            mp.mEventHandler.sendMessage(m);        }    }
这里的第一个参数mediaplayer_ref是从native_setup函数的参数,即Mediaplayer的一个弱引用,会随着Mediaplayer的释放而释放。最终,调用流又返回到了最初的Java层的Mediaplayer对象这里。

原创粉丝点击