本文共 22597 字,大约阅读时间需要 75 分钟。
本系列文章分析的安卓源码版本:【Android 10.0 版本】
一、MediaPlayer的简单调用关键流程
【这只是一个简单使用过程示例】// 此处变量声明省略    private void play() {       // 先获取SurfaceHolder        mSurfaceView = findViewById(R.id.surface_view);        mSurfaceHolder = mSurfaceView.getHolder();        mSurfaceHolder.addCallback(mSurfaceHolderCallback);                // 创建播放器        mMediaPlayer = new MediaPlayer();                // 设置各种监听事件        mMediaPlayer.setOnPreparedListener(mPreparedListener);        mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);        mMediaPlayer.setOnCompletionListener(mCompletionListener);        mMediaPlayer.setOnErrorListener(mErrorListener);        mMediaPlayer.setOnInfoListener(mInfoListener);        mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);                mAudioSessionId = AudioManager.generateAudioSessionId();        // 音频播放属性,该变量使用来向AudioManager进行申请AudioFocus使用的,通常情况下需要遵守安卓系统的audio focus机制        mAudioAttributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA)                .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE).build();        mMediaPlayer.setAudioAttributes(mAudioAttributes);        mMediaPlayer.setAudioSessionId(mAudioSessionId);                // 设置数据源        mMediaPlayer.setDataSource(mPath);        // 设置视频显示surface        mMediaPlayer.setDisplay(mSurfaceHolder);        mMediaPlayer.setScreenOnWhilePlaying(true);        // 推荐都使用异步进行,然后在【mPreparedListener】异步监听回调中,        // 调用start()方法【mMediaPlayer.start();】即可开始播放        mMediaPlayer.prepareAsync();    }   如上调用步骤,可将分析按照这些步骤顺序分析即可。
先不分析SurfaceView,因此从【new MediaPlayer()】初始化和创建MediaPlayer对象开始分析。主要处理流程如下:
1、MediaPlayer的初始化和创建源码实现分析;【本章内容分析】 2、; 3、; 4、; 5、; 6、; 7、setScreenOnWhilePlaying方法实现流程分析;【TODO】大致先分为以上处理流程分析
二、MediaPlayer的初始化和创建源码实现分析
1、在MediaPlayer.java类中,有这样一段静态代码:// [android/media/MediaPlayer.java]    static {       	// 加载系统中名叫media_jni的so动态库,该库位于系统system目录中    	// 【/system/lib64/libmedia_jni.so】和【/system/lib/libmedia_jni.so】    	// 该库就是MediaPlayer.java调用实现的JNI层库实现,    	// 用于和C++底层MediaPlayer进行相互链接调用。    	// 该实现代码实际上就是对应后面分析的jni层实现,    	// 例如[framework/base/media/jni/android_media_MediaPlayer.cpp]    	// 该加载具体流程实现不在我们的分析范围内        System.loadLibrary("media_jni");        // native层jni方法的调用,初始化native层        // 见1.1小节分析        native_init();    }   该代码的执行时机是,加载类class对象时执行,并且每个类只会执行一次,但可以创建多个类对象。
由此可知此处代码执行,早于类对象的创建时机。因此需先分析。1.1、native_init()实现分析:
由安卓常用jni调用流程可知,native层jni的实现文件应该是包名加类名,中间用下划线组成的cpp文件。即如下文件中找到该方法:// [framework/base/media/jni/android_media_MediaPlayer.cpp]// 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 voidandroid_media_MediaPlayer_native_init(JNIEnv *env){   // 根据注释可知,该方法只有在java层MediaPlayer类第一次class类加载时才会唯一执行一次,// 主要就是关联Java层MediaPlayer类的一些字段和方法,用于访问这些信息    jclass clazz;	// 获取该类加载的class类对象    clazz = env->FindClass("android/media/MediaPlayer");    if (clazz == NULL) {           return;    }    // 获取该类加载的class类对象字段表中【mNativeContext】字段的字段ID索引,    // 以供后续访问和修改该值(经过后面的分析可知该值存储的是native层MediaPlayer对象的指针值)。    // 备注:java层声明为【private long mNativeContext;】    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");    if (fields.context == NULL) {           return;    }    // 类似上面处理,此处获取class类对象方法表中的【postEventFromNative】静态方法的方法索引,以供后续调用    // 备注:该方法是Java层MediaPlayer内部用于接收native层的通知事件    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");    if (fields.post_event == NULL) {           return;    }    // 关联Java层MediaPlayer的 mNativeSurfaceTexture 字段    // 备注:用于缓存native层的surface信息    fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");    if (fields.surface_texture == NULL) {           return;    }    // 释放上面native创建的class局部变量    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);    // 此处为视频数字版权管理信息,一般用不上,因此暂不分析    // (安卓高版本可用)    // Modular DRM    FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");    if (clazz) {           GET_METHOD_ID(gStateExceptionFields.init, clazz, "       ", "(ILjava/lang/String;)V");        gStateExceptionFields.classId = static_cast         (env->NewGlobalRef(clazz));        env->DeleteLocalRef(clazz);    } else {           ALOGE("JNI android_media_MediaPlayer_native_init couldn't "              "get clazz android/media/MediaDrm$MediaDrmStateException");    }    // 最后初始化三个结构全局变量,关联这三个结构体对应的Java层这三个对象字段    // 或方法索引信息,可用于后续操纵    // 备注,在C++中可将结构类型看作类来处理,其声明如下:    /*	static PlaybackParams::fields_t gPlaybackParamsFields;	static SyncParams::fields_t gSyncParamsFields;	static VolumeShaperHelper::fields_t gVolumeShaperFields;	*/	// 因此找到这三个结构对应的init方法实现	// 播放参数字段信息,见1.2小节分析    gPlaybackParamsFields.init(env);    // 音视频同步参数信息,见1.3小节分析    gSyncParamsFields.init(env);    // 音量调节信息,见1.4小节分析    gVolumeShaperFields.init(env);}            1.2、gPlaybackParamsFields.init(env)实现分析:
// [framework/base/media/jni/android_media_PlaybackParams.h]        void init(JNIEnv *env) {               // 获取加载Java层PlaybackParams该类的class对象信息【此为local局部引用】            jclass lclazz = env->FindClass("android/media/PlaybackParams");            if (lclazz == NULL) {                   return;            }            // 创建该class对象对应的native层全局引用,并缓存在native层该对象中            clazz = (jclass)env->NewGlobalRef(lclazz);            if (clazz == NULL) {                   return;            }            // 缓存java层PlaybackParams该类的无参数构造函数的函数索引,后续使用            constructID = env->GetMethodID(clazz, "       ", "()V");						// 下面不再一一分析,主要都是缓存该类对象中对应的变量字段索引,后续使用			            speed = env->GetFieldID(clazz, "mSpeed", "F");            pitch = env->GetFieldID(clazz, "mPitch", "F");            audio_fallback_mode = env->GetFieldID(clazz, "mAudioFallbackMode", "I");            audio_stretch_mode = env->GetFieldID(clazz, "mAudioStretchMode", "I");            set = env->GetFieldID(clazz, "mSet", "I");            set_speed =                env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_SPEED", "I"));            set_pitch =                env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_PITCH", "I"));            set_audio_fallback_mode = env->GetStaticIntField(                    clazz, env->GetStaticFieldID(clazz, "SET_AUDIO_FALLBACK_MODE", "I"));            set_audio_stretch_mode = env->GetStaticIntField(                    clazz, env->GetStaticFieldID(clazz, "SET_AUDIO_STRETCH_MODE", "I"));            // 释放创建的局部变量            env->DeleteLocalRef(lclazz);        }       1.3、gSyncParamsFields.init(env)实现分析:
该方法处理同上1.2小节中的播放参数字段处理类似,也是获取并缓存Java层SyncParams类对象对应的字段或方法索引,以供后续使用// [framework/base/media/jni/android_media_SyncParams.cpp]void SyncParams::fields_t::init(JNIEnv *env) {       jclass lclazz = env->FindClass("android/media/SyncParams");    if (lclazz == NULL) {           return;    }    clazz = (jclass)env->NewGlobalRef(lclazz);    if (clazz == NULL) {           return;    }    constructID = env->GetMethodID(clazz, "       ", "()V");    sync_source = env->GetFieldID(clazz, "mSyncSource", "I");    audio_adjust_mode = env->GetFieldID(clazz, "mAudioAdjustMode", "I");    tolerance = env->GetFieldID(clazz, "mTolerance", "F");    frame_rate = env->GetFieldID(clazz, "mFrameRate", "F");    set = env->GetFieldID(clazz, "mSet", "I");    set_sync_source =        env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_SYNC_SOURCE", "I"));    set_audio_adjust_mode = env->GetStaticIntField(            clazz, env->GetStaticFieldID(clazz, "SET_AUDIO_ADJUST_MODE", "I"));    set_tolerance =        env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_TOLERANCE", "I"));    set_frame_rate =        env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_FRAME_RATE", "I"));    env->DeleteLocalRef(lclazz);}       1.4、gVolumeShaperFields.init(env)实现分析:
该方法处理同上1.2小节中的播放参数字段处理类似,也是获取并缓存Java层android/media/VolumeShaper类对象及其类部内中对应的字段或方法索引,以供后续使用// [framework/base/media/jni/android_media_VolumeShaper.h]        void init(JNIEnv *env) {               jclass lclazz = env->FindClass("android/media/VolumeShaper$Configuration");            if (lclazz == nullptr) {                   return;            }            coClazz = (jclass)env->NewGlobalRef(lclazz);            if (coClazz == nullptr) {                   return;            }            coConstructId = env->GetMethodID(coClazz, "       ", "(IIIDI[F[F)V");            coTypeId = env->GetFieldID(coClazz, "mType", "I");            coIdId = env->GetFieldID(coClazz, "mId", "I");            coOptionFlagsId = env->GetFieldID(coClazz, "mOptionFlags", "I");            coDurationMsId = env->GetFieldID(coClazz, "mDurationMs", "D");            coInterpolatorTypeId = env->GetFieldID(coClazz, "mInterpolatorType", "I");            coTimesId = env->GetFieldID(coClazz, "mTimes", "[F");            coVolumesId = env->GetFieldID(coClazz, "mVolumes", "[F");            env->DeleteLocalRef(lclazz);            lclazz = env->FindClass("android/media/VolumeShaper$Operation");            if (lclazz == nullptr) {                   return;            }            opClazz = (jclass)env->NewGlobalRef(lclazz);            if (opClazz == nullptr) {                   return;            }            opConstructId = env->GetMethodID(opClazz, "         ", "(IIF)V");            opFlagsId = env->GetFieldID(opClazz, "mFlags", "I");            opReplaceIdId = env->GetFieldID(opClazz, "mReplaceId", "I");            opXOffsetId = env->GetFieldID(opClazz, "mXOffset", "F");            env->DeleteLocalRef(lclazz);            lclazz = env->FindClass("android/media/VolumeShaper$State");            if (lclazz == nullptr) {                   return;            }            stClazz = (jclass)env->NewGlobalRef(lclazz);            if (stClazz == nullptr) {                   return;            }            stConstructId = env->GetMethodID(stClazz, "           ", "(FF)V");            stVolumeId = env->GetFieldID(stClazz, "mVolume", "F");            stXOffsetId = env->GetFieldID(stClazz, "mXOffset", "F");            env->DeleteLocalRef(lclazz);        }                  2、分析Java层MediaPlayer创建流程
2.1、分析MediaPlayer的构造函数:// [android/media/MediaPlayer.java] /** * Default constructor. Consider using one of the create() methods for * synchronously instantiating a MediaPlayer from a Uri or resource. *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.
*/ public MediaPlayer() { // 此处调用了父类构造函数,见2.2小节分析 super(new AudioAttributes.Builder().build(), AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER); // 创建Handler并设置其looper信息,即handler任务应该在哪个线程中执行 // EventHandler该类主要是用于接收native层回调事件后的处理,见第3小节分析 Looper looper; if ((looper = Looper.myLooper()) != null) { // 当前线程(子线程或主线程)中执行任务 mEventHandler = new EventHandler(this, looper); } else if ((looper = Looper.getMainLooper()) != null) { // 主线程中执行任务 mEventHandler = new EventHandler(this, looper); } else { // 不接收MediaPlayer底层的处理事件结果 mEventHandler = null; } // 该类定义为【static class TimeProvider implements MediaPlayer.OnSeekCompleteListener,MediaTimeProvider】,并且是个内部隐藏类,主要处理只是MediaPlayer内部对seek操作完成和媒体时间信息的处理流程,因此暂不分析 mTimeProvider = new TimeProvider(this); // 此为打开的字幕流,可能由多种字幕流,因此暂不分析 mOpenSubtitleSources = new Vector(); /* Native setup requires a weak reference to our object. * It's easier to create it here than in C++. */ // native层初始化,并将当前MediaPlayer对象的弱引用传递给native层进行绑定 // 见2.3小节分析 native_setup(new WeakReference (this)); // 见2.4小节分析 baseRegisterPlayer(); } 
2.2、MediaPlayer父类PlayerBase
MediaPlayer类的声明:// [android/media/MediaPlayer.java]public class MediaPlayer extends PlayerBase                         implements SubtitleController.Listener                                  , VolumeAutomation                                  , AudioRouting {   }   由上可知Media Player继承自一个播放器公共类 PlayerBase,并实现了三个接口(接口暂时不用分析),因此查看PlayerBase对应被初始化的构造函数:
// [android/media/PlayerBase.java]/**     * Constructor. Must be given audio attributes, as they are required for AppOps.     * @param attr non-null audio attributes     * @param class non-null class of the implementation of this abstract class     */    PlayerBase(@NonNull AudioAttributes attr, int implType) {           if (attr == null) {               throw new IllegalArgumentException("Illegal null AudioAttributes");        }        // 此处只是将音频播放属性信息保存下来,并设置播放配置状态为空闲状态        mAttributes = attr;        mImplType = implType;        mState = AudioPlaybackConfiguration.PLAYER_STATE_IDLE;    };   由该类声明,可知该类是隐藏类,不提供给外部使用。
2.3、native_setup(new WeakReference(this))实现分析:
由安卓常用jni调用流程可知,native层jni的实现文件应该是包名加类名,中间用下划线组成的cpp文件。即如下文件中找到该方法:// [framework/base/media/jni/android_media_MediaPlayer.cpp]static voidandroid_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this){       ALOGV("native_setup");    // 创建一个native层的MediaPlayer对象与Java层的对象进行绑定    // 该类的声明和构造函数初始化,见2.3.1小节分析    sp        mp = new MediaPlayer();    if (mp == NULL) {           jniThrowException(env, "java/lang/RuntimeException", "Out of memory");        return;    }    // 创建native层回调事件给Java层的listener,并在其构造函数中创建java层MediaPlayer对应的全局变量    // 见2.3.2小节分析    // create new listener and give it to MediaPlayer    sp          listener = new JNIMediaPlayerListener(env, thiz, weak_this);    mp->setListener(listener);	// 设置native层MediaPlayer对象唯一标识值给到Java层MediaPlayer对象保存,以此来绑定关系    // 见2.3.3小节分析    // Stow our new C++ MediaPlayer in an opaque field in the Java object.    setMediaPlayer(env, thiz, mp);}            2.3.1、native层MediaPlayer对象的声明和构造函数分析:
对象声明:// [framework/av/include/media/meidaplayer.h]class MediaPlayer : public BnMediaPlayerClient,                    public virtual IMediaDeathNotifier{       // 省略其他	// 由声明可知,MediaPlayer继承了两个父类【C++允许多继承,Java中只能单继承】	// 由android native层Binder机制可知,BnMediaPlayerClient是功能实现端,	// 那么肯定存在一个功能代码端 BpMediaPlayerClient,此处先不分析	// IMediaDeathNotifier类则是用于Binder通信时监测Binder通信是否异常关闭,	// 若关闭则通知java层MediaPlayer端native层Binder died事件【调用MediaPlayer::died()方法】,暂不分析}   关于C++ Binder机制知识点可参考我的另一篇文章:
对象的构造函数分析:
其实就是一些字段默认值的初始化MediaPlayer::MediaPlayer(){       ALOGV("constructor");    mListener = NULL;    mCookie = NULL;    // 默认音频流类型为MUSIC    mStreamType = AUDIO_STREAM_MUSIC;    mAudioAttributesParcel = NULL;    mCurrentPosition = -1;    // 默认当前seek模式为seek位置前SYNC同步,默认寻找seek位置前的关键帧数据进行播放    mCurrentSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;    mSeekPosition = -1;    mSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;    // 初始化当前播放状态:空闲状态    mCurrentState = MEDIA_PLAYER_IDLE;    mPrepareSync = false;    mPrepareStatus = NO_ERROR;    mLoop = false;    mLeftVolume = mRightVolume = 1.0;    mVideoWidth = mVideoHeight = 0;    mLockThreadId = 0;    // Audio回话ID,默认重新分配一个,从上面java层分析可知,java层也可以设置该值    mAudioSessionId = (audio_session_t) AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);    AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);    mSendLevel = 0;    mRetransmitEndpointValid = false;}   2.3.2、JNIMediaPlayerListener构造函数实现分析:
// [framework/base/media/jni/android_media_MediaPlayer.cpp]JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz){       // Hold onto the MediaPlayer class for use in calling the static method    // that posts events to the application thread.    // 获取Java层MediaPlayer对象对应的class对象    jclass clazz = env->GetObjectClass(thiz);    if (clazz == NULL) {           ALOGE("Can't find android/media/MediaPlayer");        jniThrowException(env, "java/lang/Exception", NULL);        return;    }    // 将其局部class对象转换为全局class对象,用于后续static函数调用    mClass = (jclass)env->NewGlobalRef(clazz);	// 将Java层MediaPlayer对象的弱引用对象转换为native层对应的全局对象,因为该Java对象是弱引用对象,因此不必担心垃圾回收器不能回收它。    // We use a weak reference so the MediaPlayer object can be garbage collected.    // The reference is only used as a proxy for callbacks.    mObject  = env->NewGlobalRef(weak_thiz);}   2.3.3、setMediaPlayer实现分析:
// [framework/base/media/jni/android_media_MediaPlayer.cpp]static spsetMediaPlayer(JNIEnv* env, jobject thiz, const sp & player){ // 加锁访问下面的代码,C++的自动锁功能【不展开分析】 Mutex::Autolock l(sLock); // 获取全局变量中缓存的native层Media Player对象的指针 // 备注:fields是一个native层的static全局变量,其是一个结构体, // 缓存一些Java层MediaPlayer对象的字段和方法引用,以此来操纵它们的值 sp old = (MediaPlayer*)env->GetLongField(thiz, fields.context); // 关于Android上C++的智能指针知识请查看系统文件的相关实现 // 实现文件路径在【system/core/libutils/】 // 主要文件为:StrongPointer.h、RefBase.cpp if (player.get()) { // 不为空时即当前native层MediaPlayer的指针对象存在时, // 将当前方法的方法指针引用和该对象进行强引用绑定,即增加引用计数 // 备注:首先需要知道player对象被声明为了一个智能指针,其可以不需要手动维护它的强引用计数, // 但此处做了这个处理的原因是,该player对象的原始指针即对象本身的 // 指针【通过get()获取的】会被直接跳过sp智能指针的功能限制,直接缓存到全局变量【fields.context】中了, // 因此需要将其增加引用,否则在当前方法执行完毕后sp智能指针功能将会执行decStrong, // 也就是说player指针的引用计数会被减一,若此处不增加其引用计数, // 那么则会在函数执行完毕后,player对象指针就被释放了。 player->incStrong((void*)setMediaPlayer); } if (old != 0) { // 若此前该对象指针已存在时,则必现先解除它和当前方法引用的绑定,使其可以被正常, // 即减少该对象指针的引用计数,让其内存能被正常释放。 old->decStrong((void*)setMediaPlayer); } // 缓存新的native层MediaPlayer原始对象的指针值,通过前面的init过程分析, // 可知该值缓存在了全局变量的【fields.context】字段中, // 而该字段指向的是Java层MediaPlayer对象的【mNativeContext】long类型字段,因此该值同时被缓存到Java层 env->SetLongField(thiz, fields.context, (jlong)player.get()); // 返回旧对象 return old;} 
2.4、baseRegisterPlayer()实现分析:
该方法为父类方法。// [android.media.PlayerBase.java]   /**     * Call from derived class when instantiation / initialization is successful     */    protected void baseRegisterPlayer() {       	// 由该常量定义可知,该常量值为true,因此不会执行if内部代码        if (!USE_AUDIOFLINGER_MUTING_FOR_OP) {               IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);            mAppOps = IAppOpsService.Stub.asInterface(b);            // initialize mHasAppOpsPlayAudio            updateAppOpsPlayAudio();            // register a callback to monitor whether the OP_PLAY_AUDIO is still allowed            mAppOpsCallback = new IAppOpsCallbackWrapper(this);            try {                   mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO,                        ActivityThread.currentPackageName(), mAppOpsCallback);            } catch (RemoteException e) {                   Log.e(TAG, "Error registering appOps callback", e);                mHasAppOpsPlayAudio = false;            }        }                // PlayerIdCard 和 IPlayerWrapper 这两个类是PlayerBase中的两个内部类,        // 此处为获取当前player对象被Audio服务track的播放器唯一ID。        // 该功能主要作用就是用于AudioFlinger跟踪当前播放器的状态信息等        // 如生成PIID、获取UID、PID、音频播放状态事件等音频配置信息缓存到音频播放配置信息对象中,然后可以进行log debug        try {               mPlayerIId = getService().trackPlayer(                    new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));        } catch (RemoteException e) {               Log.e(TAG, "Error talking to audio service, player will not be tracked", e);        }    }   3、EventHandler该类实现分析:
【其实就是Handler机制,实现原理可见我另一章节分析()】 该类为MediaPlayer内部类,主要是用于接收native层回调事件后的处理。 构造函数:// [android.media.MediaPlayer.java]public EventHandler(MediaPlayer mp, Looper looper) {               super(looper);            // 缓存当前MediaPlayer对象            mMediaPlayer = mp;        }   Handler机制事件接收处理:
// [android.media.MediaPlayer.java]        @Override        public void handleMessage(Message msg) {           	// 由上面的分析可知,【mNativeContext】字段缓存的值是native层MediaPlayer对象指针值            if (mMediaPlayer.mNativeContext == 0) {                   Log.w(TAG, "mediaplayer went away with unhandled events");                return;            }            // 所有的底层回调事件都会在此处通过事件类型【不同int值标识】来处理            // 定义了非常多的事件回调类型,但目前只开放了几个listener事件给应用层使用,            // 即可以通过设置setListener的方法监听回调事件,其他事件都是此处内部处理的。            switch(msg.what) {               case MEDIA_PREPARED:                try {                       scanInternalSubtitleTracks();                } catch (RuntimeException e) {                       // send error message instead of crashing;                    // send error message instead of inlining a call to onError                    // to avoid code duplication.                    Message msg2 = obtainMessage(                            MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);                    sendMessage(msg2);                }                OnPreparedListener onPreparedListener = mOnPreparedListener;                if (onPreparedListener != null)                    onPreparedListener.onPrepared(mMediaPlayer);                return;                                // ... 省略其他事件回调处理   目前接收的事件类型有:
根据注释,我们可知这些事件类型的定义必须和native层【include/media/mediaplayer.h】该文件中的定义保持一致才有效/* Do not change these values without updating their counterparts * in include/media/mediaplayer.h! */ private static final int MEDIA_NOP = 0; // interface test message private static final int MEDIA_PREPARED = 1; private static final int MEDIA_PLAYBACK_COMPLETE = 2; private static final int MEDIA_BUFFERING_UPDATE = 3; private static final int MEDIA_SEEK_COMPLETE = 4; private static final int MEDIA_SET_VIDEO_SIZE = 5; private static final int MEDIA_STARTED = 6; private static final int MEDIA_PAUSED = 7; private static final int MEDIA_STOPPED = 8; private static final int MEDIA_SKIPPED = 9; private static final int MEDIA_NOTIFY_TIME = 98; private static final int MEDIA_TIMED_TEXT = 99; private static final int MEDIA_ERROR = 100; private static final int MEDIA_INFO = 200; private static final int MEDIA_SUBTITLE_DATA = 201; private static final int MEDIA_META_DATA = 202; private static final int MEDIA_DRM_INFO = 210; private static final int MEDIA_TIME_DISCONTINUITY = 211; private static final int MEDIA_AUDIO_ROUTING_CHANGED = 10000;
本章结束语:
本章已分析完MediaPlayer的初始化和创建处理流程,本系列后续章节请查看后续章节分析转载地址:http://jopmz.baihongyu.com/