目录
前言
Android中经常用MediaPlayer控制音频/视频文件和流的播放。虽然经常用,但没怎么看其源码,今天有空记录一下,方便自己查阅。
正文
本篇涉及的源码目录
frameworks\base\media\java\android\media\MediaPlayer.java
进入正题是先看看MediaPlayer中涉及的状态。(图片来源《》)
demo
下面是播放音频demo
初始化和设置需要的监听
if (null == mMediaPlayer) { mMediaPlayer = new MediaPlayer(); mMediaPlayer.setOnCompletionListener(this);//播放完监听 mMediaPlayer.setOnErrorListener(this);//播放异常 mMediaPlayer.setOnInfoListener(this);//播放状态 mMediaPlayer.setOnPreparedListener(this);//准备好状态 mMediaPlayer.setOnSeekCompleteListener(this);//seek完成 } try { mMediaPlayer.setDataSource("/storage/udisk0/黄昏-周传雄.flac"); //mMediaPlayer.prepareAsync(); mMediaPlayer.prepare(); } catch (IOException e) { throw new RuntimeException(e); }
监听只附上一个,需要在准备好后开启播放
@Override public void onPrepared(MediaPlayer mp) { //加载完就开启播放 mMediaPlayer.start(); }
注销
if (null != mMediaPlayer) { mMediaPlayer.reset(); mMediaPlayer.release(); mMediaPlayer = null; }
MediaPlayer状态
Idle 播放器实例化或调用reset()后的状态 End 播放器release()调用之后的状态 Error 播放器出错的状态,一般情况是在调用方法和初始化错误时发生 Initialized 播放器调用setDataSource()时 preparing 播放器调用prepareAsync()或prepare()时的状态,时间较短 prepared 播放器调用prepareAsync()或prepare()时经历preparing Started 播放器调用start()方法后 Paused 播放器调用pause()方法后 Stopped 播放器调用stop()方法后 PlaybackCompleted 在播放器没有设置Loop循环模式下,播放一次后表示播放结束
播放状态内容来之《》
MediaPlayer.java
public class MediaPlayer extends PlayerBase implements SubtitleController.Listener , VolumeAutomation, AudioRouting{ //略 }
从上面知道MediaPlayer继承于PlayerBase,并实现如下接口
SubtitleController.Listener 字幕控制监听 VolumeAutomation 音量调节[字面意思,具体没深究] AudioRouting 音频控制[字面意思,具体没深究]
而PlayerBase是常用播放状态封装,具体可以自己看。
在看源码时一般会看静态代码块是否存在,然后是构造函数,再后来就是demo中调用的方法。
每个人可能不一样,勿喷!
static {}
MediaPlayer.java中有静态代码块
static { //加载libmedia_jni.so System.loadLibrary("media_jni"); native_init(); }
MediaPlayer()
public MediaPlayer() { //初始化父类 super(new AudioAttributes.Builder().build(), AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER); //初始化Looper和EventHandler Looper looper; if ((looper = Looper.myLooper()) != null) { mEventHandler = new EventHandler(this, looper); } else if ((looper = Looper.getMainLooper()) != null) { mEventHandler = new EventHandler(this, looper); } else { mEventHandler = null; } //本地方法 native_setup(new WeakReference<MediaPlayer>(this)); //父类中实现的 baseRegisterPlayer(); }
不纠结细节,下面是设置监听
mMediaPlayer.setOnCompletionListener(this);//播放完监听 mMediaPlayer.setOnErrorListener(this);//播放异常 mMediaPlayer.setOnInfoListener(this);//播放状态 mMediaPlayer.setOnPreparedListener(this);//准备好状态 mMediaPlayer.setOnSeekCompleteListener(this);//seek完成
监听的流程都一样,这里以setOnCompletionListener为例。
setOnCompletionListener
public void setOnCompletionListener(OnCompletionListener listener){ mOnCompletionListener = listener; } private OnCompletionListener mOnCompletionListener;
被调用的地方在EventHandler的handleMessage中
public void handleMessage(Message msg) { switch(msg.what) { case MEDIA_PLAYBACK_COMPLETE:{ //播放结束 mOnCompletionInternalListener.onCompletion(mMediaPlayer); OnCompletionListener onCompletionListener = mOnCompletionListener; if (onCompletionListener != null) onCompletionListener.onCompletion(mMediaPlayer); } stayAwake(false); return; case MEDIA_ERROR: boolean error_was_handled = false; OnErrorListener onErrorListener = mOnErrorListener; if (onErrorListener != null) { error_was_handled = onErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2); } { //如果播放错误没有处理,就返回播放结束 mOnCompletionInternalListener.onCompletion(mMediaPlayer); OnCompletionListener onCompletionListener = mOnCompletionListener; if (onCompletionListener != null && ! error_was_handled) { onCompletionListener.onCompletion(mMediaPlayer); } } stayAwake(false); return; } }
什么时候回调?
-
播放文件结束
-
播放文件出错,且错误没有被处理时
mEventHandler的消息最终是从postEventFromNative()来的
private static void postEventFromNative(Object mediaplayer_ref, int what, int arg1, int arg2, Object obj){ final MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get(); if (mp == null) { return; } if (mp.mEventHandler != null) { Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj); mp.mEventHandler.sendMessage(m); } }
从名字看就知道来自JNI层,暂不深入,后续分析。
setDataSource()
setDataSource重载了很多方法,这里以demo中调用的为例
public void setDataSource(String path){ setDataSource(path, null, null); }
绕了好几个setDataSource()方法进行封装,最终调用的是
private native void _setDataSource(FileDescriptor fd, long offset, long length);
也就是有进入JNI,这里先跳过。
prepare() 或prepareAsync()
//对于文件,可以调用prepare(),它会阻塞直到MediaPlayer准备好播放。 prepare();
//对于流,应该调用prepareSync(),它会立即返回,而不是阻塞直到缓冲了足够的数据。 prepareAsync();
播放本地文件(文件可能很大),为了快速播放会优先考虑prepareAsync(),但系统IO高的时候就可能出现卡顿问题。
只能说各有千秋,看出现的情况而定。
prepare()
public void prepare() { _prepare(); } private native void _prepare();
prepareAsync()
public native void prepareAsync();
最终还是进入JNI了。
start()
demo中在onPrepared()回调后开启播放的
public void onPrepared(MediaPlayer mp) { //加载完就开启播放 mMediaPlayer.start(); }
public void start() throws IllegalStateException { final int delay = getStartDelayMs(); //是否需要延迟播放,可以通过setStartDelayMs()设置 //默认delay=0; if (delay == 0) { startImpl(); } else { new Thread() { public void run() { try { Thread.sleep(delay); } catch (InterruptedException e) { e.printStackTrace(); } //延迟后恢复默认值0 baseSetStartDelayMs(0); try { startImpl(); } catch (IllegalStateException e) { } } }.start(); } } private void startImpl() { //更新播放状态和判断是否需要mute baseStart(); _start(); } private native void _start() throws IllegalStateException;
reset()
public void reset() { stayAwake(false); //本地方法 _reset(); //移除Handler消息 if (mEventHandler != null) { mEventHandler.removeCallbacksAndMessages(null); } } private native void _reset();
reset()就是清除上一个所有状态,此时为Idle。上下曲时,如果复用MediaPlayer,一定要reset()一下。
release()
public void release() { //调用PlayerBase释放相关资源 baseRelease(); //监听全部置为null mOnPreparedListener = null; mOnBufferingUpdateListener = null; mOnCompletionListener = null; mOnSeekCompleteListener = null; mOnErrorListener = null; mOnInfoListener = null; mOnVideoSizeChangedListener = null; mOnTimedTextListener = null; //本地方法 _release(); } private native void _release();
如果是停止播放,就需要释放资源。
由于release中没有清除Handler消息,因此reset()和release()需要配合使用。
mMediaPlayer.reset(); mMediaPlayer.release();
参考文章
-
《