本文共 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小节分析 spmp = 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/