努力尽今夕,少年犹可夸。这篇文章主要讲述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 {
jfieldIDcontext;
jfieldIDsurface_texture;
jmethodIDpost_event;
jmethodIDproxyConfigGetHost;
jmethodIDproxyConfigGetPort;
jmethodIDproxyConfigGetExclusionList;
};
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变量中
}
该函数中主要做了三个操作:
【Android -- MediaPlayer内部实现简析】
- 创建了一个Native MediaPlayer对象
- 创建了一个JNIMediaPlayerListener对象, 它主要用于向上层MediaPlayer( .java) 对象通知事件或抛出异常
- 将创建的Native MediaPlayer实例保存到MediaPlayer.java::mNativeContext字段中
推荐阅读
- callee(),call(),apply()方法的使用
- idea android 开发
- AndroidMPAndroidCharts 框架 画可滑动查看的直方图
- Android绘制优化布局优化
- Android 屏幕适配扫盲教程
- HTML输入目录名属性input-dirname
- 如何使用jQuery检查元素是否包含类()
- 算法设计(最大和连续子数组的范围查询)
- 在链表中找到最大和第二大的值