博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【一】Android MediaPlayer整体架构源码分析 -【初始化和创建】
阅读量:682 次
发布时间:2019-03-15

本文共 22648 字,大约阅读时间需要 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 sp
setMediaPlayer(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/

你可能感兴趣的文章