Android -- MediaPlayer内部实现简析
Posted 第一序列丶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android -- MediaPlayer内部实现简析相关的知识,希望对你有一定的参考价值。
android -- MediaPlayer内部实现简析
在之前的博客中,已经介绍了使用MediaPlayer时要注意的内容。现在,这里就通过一个MediaPlayer代码实例,来进一步分析MediaPlayer内部是如何运作、实现的;当然这里的分析只截止到底层调用播放器之前,因为播放器这块实在是没搞懂。
我们使用的例子来源于之前MediaPlayer Playback译文中的官方实例:
String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudiostreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();
代码中主要通过5个步骤实现了媒体的播放过程,我们一步步来分析。
一、创建MediaPlayer
从MediaPlayer模块的实现层次来说,它其实只是一个暴露给外部调用的工具类;真正的媒体操作都通过JNI调用到底层Media服务,由它们真正实现。MediaPlayer类要使用一个libmedia_jni.so库,它的加载过程如下:
static {
System.loadLibrary("media_jni");
native_init();
}
libmedia_jni.so提供了MediaPlayer需要调用的各个JNI函数,它对应的文件是android_media_MediaPlayer.cpp。load该so库的同时,会调用native_init()函数进行一些前期的初始化工作:
// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in MediaPlayer, which won't run until the
// first time an instance of this class is used.
static void
android_media_MediaPlayer_native_init(JNIEnv *env)//初始化一些Field和Method域ID
{
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);
}
struct fields_t {
jfieldID context;
jfieldID surface_texture;
jmethodID post_event;
jmethodID proxyConfigGetHost;
jmethodID proxyConfigGetPort;
jmethodID proxyConfigGetExclusionList;
};
static fields_t fields;
从代码可知,native_init()函数主要保存了一些MediaPlayer.java中定义的一些字段或方法的ID;其中获取的mNativeContext字段,用于将初始化的本地MediaPlayer对象的地址保存到该变量中,这也就给每一个MediaPlayer.java实例绑定了一个Native层的MediaPlayer;另外,post_event保存了MediaPlayer::postEventFromNative()函数的ID值,它会被用来在Native层中向上层抛出事件或异常。
加载完要使用的动态库,我们就可以开始创建MediaPlayer实例了,首先看它的默认构造函数:
/**
* Default constructor. Consider using one of the create() methods for
* synchronously instantiating a MediaPlayer from a Uri or resource.
* <p>When done with the MediaPlayer, you should call {@link #release()},
* to free the resources. If not released, too many MediaPlayer instances may
* result in an exception.</p>
*/
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()函数
}
我们的MediaPlayer需要运行在消息循环中,EventHandler是MediaPlayer的一个内部类,它专门处理来自Native层的事件,这些事件一般都表明了MediaPlayer现在转移到了某个状态,我们可以在该状态处理什么回调操作。EventHandler的功能较为单一,就是根据底层上抛的事件,进行对应的回调或事件处理,这里就不再细看。接着,调用了native_setup()函数,并传入了一个MediaPlayer类型的弱引用实例,我们看该函数的实现:
static void
android_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
//JNIMediaPlayerListener类继承自MediaPlayer.h中声明的MediaPlayerListener,并实现了notify()方法
sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
mp->setListener(listener);//在Native MediaPlayer实例中保存这个JNIMediaPlayerListener监听对象
// Stow our new C++ MediaPlayer in an opaque field in the Java object.
setMediaPlayer(env, thiz, mp);//将创建的Native MediaPlayer对象转化成Long型值(地址),保存到MediaPlayer.java::mNativeContext变量中
}
该函数中主要做了三个操作:
- 创建了一个Native MediaPlayer对象
- 创建了一个JNIMediaPlayerListener对象,它主要用于向上层MediaPlayer(.java)对象通知事件或抛出异常
- 将创建的Native MediaPlayer实例保存到MediaPlayer.java::mNativeContext字段中
Native MediaPlayer类继承自BnMediaPlayerClient,它是IMediaPlayerClient业务的服务端。IMediaPlayerClient业务的整个框架如图所示:
以上是关于Android -- MediaPlayer内部实现简析的主要内容,如果未能解决你的问题,请参考以下文章