Android MediaPlayer架构 -- 前言小知识点(一)
在Android中可以使用MediaPlayer+SurfaceView来实现一个简单的多媒体播放器。
一 构造函数
java MediaPlayer class 的源码位置:frameworks\base\media\java\android\media\MediaPlayer.java
首先看一下其构造函数:
1 public MediaPlayer() {
2
3 Looper looper;
4 if ((looper = Looper.myLooper()) != null) {
5 mEventHandler = new EventHandler(this, looper);
6 } else if ((looper = Looper.getMainLooper()) != null) {
7 mEventHandler = new EventHandler(this, looper);
8 } else {
9 mEventHandler = null;
10 }
11
12 mTimeProvider = new TimeProvider(this);
13 mOpenSubtitleSources = new Vector<InputStream>();
14 IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
15 mAppOps = IAppOpsService.Stub.asInterface(b);
16
17 /* Native setup requires a weak reference to our object.
18 * It's easier to create it here than in C++.
19 */
20 native_setup(new WeakReference<MediaPlayer>(this)); // 在实例化MediaPlayer对象时,会调用一个native_setup方法
21 }
在实例化MediaPlayer对象时,会调用一个native_setup方法:
1 private native final void native_setup(Object mediaplayer_this);
在JNI代码(frameworks\base\media\jni\android_media_MediaPlayer.cpp)中可以找到对应的native函数:
1 static void
2 android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
3 {
4 ALOGV("native_setup");
5 sp<MediaPlayer> mp = new MediaPlayer(); // 实例化一个native MediaPlayer(frameworks\av\media\libmedia\mediaplayer.cpp)
6 if (mp == NULL) {
7 jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
8 return;
9 }
10
11 // create new listener and give it to MediaPlayer
12 sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
13 mp->setListener(listener);
14
15 // Stow our new C++ MediaPlayer in an opaque field in the Java object.
16 setMediaPlayer(env, thiz, mp);
17 }
在JNI可以看到,sp<MediaPlayer> mp = new MediaPlayer() 这个native MediaPlayer会去和media service进行交互实现真正的播放功能。
二 设置Listener
在JNI 函数 android_media_MediaPlayer_native_setup 中有为MediaPlayer设置listener,目的就是通过callback的方式将player的事件上传至java层,以便用户做出对应的处理
1 // create new listener and give it to MediaPlayer
2 sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
3 mp->setListener(listener);
从上面这两行代码可以看到,首先我们实例化一个JNIMediaPlayerListener对象,然后将这个对象传递给了C++ MediaPlayer,在frameworks\av\include\media\mediaplayer.h中定义了listener的接口形式:
1 // ref-counted object for callbacks
2 class MediaPlayerListener: virtual public RefBase
3 {
4 public:
5 virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
6 };
在JNI代码中,JNIMediaPlayerListener class 正是一个MediaPlayerListener的子类,实现了notify函数,其定义如下:
// ref-counted object for callbacks
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; // Reference to MediaPlayer class
jobject mObject; // Weak ref to MediaPlayer Java object to call on
};
callback事件的传递主要是通过notify函数来完成的,下面看一下这个函数的具体实现过程:
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();
}
}
-
msg, ext1, ext2, obj 携带着事件相关信息及数据;
-
通过env->CallStaticVoidMethod()方法去回调MediaPlayer(java)的 fields.post_event 方法,并将事件信息及数据传递过去;
-
fields.post_event对应于MediaPlayer的java 方法 postEventFromNative;
-
fields.post_event是在MediaPlayer第一次加载时在android_media_MediaPlayer_native_init中初始化的;
三 小结
这一篇介绍的内容很简单,主要是对java层的MediaPlayer, JNI层,及native层的MediaPlayer如何关联起来的有个基本的认识与了解。
简单两点概述:
- java层的MediaPlayer提供了java API供用户调用实现playback功能,但其功能的实现还会串接到JNI层,在JNI层会去实例化一个native MediaPlayer,这个native MediaPlayer会和MediaPlayerService进行交互实现真正的播放功能;
- 设置listener的目的就是通过回调的方式将来自native的事件进行上传,以便用户在java层做出处理,就这篇而言可以暂时这样理解流程: MediaPlayerService-->native MediaPlayer-->JNI->java MediaPlayer ;