本系列文章分析的安卓源码版本:【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、MediaPlayer设置各种Listener的事件回调处理流程;
3、setDataSource方法实现流程分析;
4、setDisplay方法实现流程分析;
5、prepareAsync/prepare方法实现流程分析;
6、start()方法实现流程分析;
7、seek流程分析:
关于seek流程此处不再分析,请查看此前已有章节分析:其实际它就是seek流程源码的分析。
Android MediaPlayer在seek视频时可能会黑屏卡顿好几秒且进度条不动但有声音播放的问题源码解析
8、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 void
android_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<jclass>(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<InputStream>();
/* 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<MediaPlayer>(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 void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
ALOGV("native_setup");
// 创建一个native层的MediaPlayer对象与Java层的对象进行绑定
// 该类的声明和构造函数初始化,见2.3.1小节分析
sp<MediaPlayer> 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<JNIMediaPlayerListener> 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机制知识点可参考我的另一篇文章:
Android 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<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
// 加锁访问下面的代码,C++的自动锁功能【不展开分析】
Mutex::Autolock l(sLock);
// 获取全局变量中缓存的native层Media Player对象的指针
// 备注:fields是一个native层的static全局变量,其是一个结构体,
// 缓存一些Java层MediaPlayer对象的字段和方法引用,以此来操纵它们的值
sp<MediaPlayer> 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机制,实现原理可见我另一章节分析(教你手写android底层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的初始化和创建处理流程,本系列后续章节请查看后续章节分析