Android -- MediaPlayer内部实现简析

努力尽今夕,少年犹可夸。这篇文章主要讲述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字段中
Native MediaPlayer类继承自BnMediaPlayerClient, 它是IMediaPlayerClient业务的服务端。IMediaPlayerClient业务的整个框架如图所示:

    推荐阅读