博客
关于我
【一】Android MediaPlayer整体架构源码分析 -【初始化和创建】
阅读量:687 次
发布时间:2019-03-15

本文共 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 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/

你可能感兴趣的文章
Mysql 学习总结(88)—— Mysql 官方为什么不推荐用雪花 id 和 uuid 做 MySQL 主键
查看>>
Mysql 学习总结(89)—— Mysql 库表容量统计
查看>>
mysql 实现主从复制/主从同步
查看>>
mysql 审核_审核MySQL数据库上的登录
查看>>
mysql 导入 sql 文件时 ERROR 1046 (3D000) no database selected 错误的解决
查看>>
mysql 导入导出大文件
查看>>
MySQL 导出数据
查看>>
mysql 将null转代为0
查看>>
mysql 常用
查看>>
MySQL 常用列类型
查看>>
mysql 常用命令
查看>>
Mysql 常见ALTER TABLE操作
查看>>
MySQL 常见的 9 种优化方法
查看>>
MySQL 常见的开放性问题
查看>>
Mysql 常见错误
查看>>
mysql 常见问题
查看>>
MYSQL 幻读(Phantom Problem)不可重复读
查看>>
mysql 往字段后面加字符串
查看>>
mysql 快照读 幻读_innodb当前读 与 快照读 and rr级别是否真正避免了幻读
查看>>
MySQL 快速创建千万级测试数据
查看>>