基于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对象这里。
- 基于android6.0 mediaplayer框架分析
- 基于Android6.0的RIL框架层模块分析
- Android MediaPlayer框架分析
- Android6.0动态权限获取框架:RxPermission(基于RxJava2)
- 基于Android6.0的RIL底层模块分析
- 基于Android6.0的Activity加载View源码分析
- android6.0源码分析之Camera API1.0框架简介
- android6.0源码分析之Camera API1.0框架简介
- android6.0源码分析之蓝牙框架简介
- Android 源码分析之基于Stagefright的MediaPlayer播放框架[4]
- Android 源码分析之基于Stagefright的MediaPlayer播放框架[3]
- Android6.0权限分析
- Android之MediaPlayer的框架源码分析
- ANDROID6.0指纹识别框架架构
- Android6.0的phone应用源码分析(6)——RIL层框架分析
- Android6.0的phone应用源码分析(7)——RIL层框架分析2
- Android6.0的phone应用源码分析(7)——RIL层框架分析2
- Android6.0 wakelock深入分析
- [LeetCode]70. Climbing Stairs
- 【C语言】实现注释转换(c->c++)
- JZOI 3521 道路覆盖 二分答案+状压dp
- 如何在ubuntu下安装微信electronic-chat
- 选择排序
- 基于android6.0 mediaplayer框架分析
- 函数摘抄
- 12
- Apache Accumulo用户手册
- 九度1008&&HDU
- 插入排序
- 静态顺序表的实现
- Redis基本操作
- 开发板测试网口带宽方法