简单介绍MediaPlayer的基本概念,状态,经常使用的方法与监听器。java
MediaPlayer类能够用来播放音视频文件,或者是音频流。开发者能够用它来播放本地音频,或者是网络在线音频。android
MediaPlayer属于android.media
包。编程
播放控制由状态机控制。在平常生活中,咱们常见的音频状态有播放中,暂停,中止,缓冲等等。
MediaPlayer的状态有以下几种:网络
状态的切换参考官方图例。
这里稍微解释一下状态转换图片。椭圆表明MediaPlayer可能停留的状态。椭圆之间的箭头表示方法调用,状态切换的方向。单箭头表示方法同步调用,双箭头表示异步调用。异步
从图中咱们能够看出状态切换的路径和涉及到的方法。ide
当new一个MediaPlayer或者调用了reset方法,当前MediaPlayer会处于Idle状态。调用release后,会处于End状态。在这2个状态之间的状态能够看作是MediaPlayer对象的生命周期。oop
在新建立MediaPlayer和调用reset的MediaPlayer之间有一些细微的差异。
这两种状况都处于Idle状态,调用 getCurrentPosition(), getDuration(), getVideoHeight(), getVideoWidth(), setAudioAttributes(android.media.AudioAttributes), setLooping(boolean), setVolume(float, float), pause(), start(), stop(), seekTo(long, int), prepare() 或 prepareAsync()方法都会抛出错误,若是是新实例化的MediaPlayer,不会回调 OnErrorListener.onError();但若是是reset后的MediaPlayer,会回调 OnErrorListener.onError()而且转换到Error状态。ui
若是MediaPlayer对象再也不使用了,当即调用release()方法,释放内部播放器占用的资源。这些资源多是惟一的,好比硬件加速组件。若是调用release失败,可能会引发一连串的MediaPlayer实例失效。当MediaPlayer处于End状态,它就不能再转移到其它状态了。spa
new一个MediaPlayer,处于Idle状态。若是用create方法建立实例,当建立完成时处于Prepared状态。.net
一些情形可能会让MediaPlayer操做失败,好比不支持的音视频格式,分辨率太高,网络超时等等。
所以在这些情形下错误处理和恢复很是重要。有时候编程错误也会致使MediaPlayer操做错误。
开发者能够设置错误监听器setOnErrorListener(android.media.MediaPlayer.OnErrorListener)
。当错误发生时,会调用用户实现的OnErrorListener.onError()方法。
无论有没有设置监听器,错误发生时MediaPlayer会进入Error状态。
为了重复使用同一个MediaPlayer对象,可使用reset()
方法把它从Error状态恢复到Idle状态。
设置错误监听器OnErrorListener是一个好的编程习惯。开发者能够监听到播放引擎的错误通知。
有时候会抛出IllegalStateException异常,好比在错误的状态调用了prepare(), prepareAsync()方法,或是setDataSource方法。
调用setDataSource(java.io.FileDescriptor), 或者 setDataSource(java.lang.String), 或者 setDataSource(android.content.Context, android.net.Uri), 或者 setDataSource(java.io.FileDescriptor, long, long), 或者 setDataSource(android.media.MediaDataSource) 能够将MediaPlayer的状态从Idle转到Initialized状态。
若是在Idle状态以外的状态调用了setDataSource(),会抛出IllegalStateException异常。
开发者应该留意setDataSource方法抛出的IllegalArgumentException和IOException异常。
MediaPlayer在开始播放音频前必须处于Prepared状态。
MediaPlayer有同步和异步2种方式来进入Prepared状态。若是是异步的方式,会先转到Preparing状态,再转到Prepared状态。
当准备完成时,内部的播放引擎会回调用户以前设置的OnPreparedListener的onPrepared()方法。
开发者必须注意的是,Preparing状态是一个过渡状态(transient state)。
处于Prepared状态时,能够经过相对应的方法设置音量,屏幕常亮,播放循环等。
播放音频必须调用start()方法。调用start()返回成功后,MediaPlayer处于Started状态。
能够经过isPlaying()来判断当前是否在Started状态。
若是开发者设置了OnBufferingUpdateListener,Android内部播放器会向外传递buffer信息。
若是当前处于Started状态,再调用start()方法没有效果。
音频能够被暂停播放和继续播放,也能够调整播放的位置。经过pause()方法来暂停音频播放。
成功调用pause()方法后,MediaPlayer进入Paused状态。
应当注意的是,MediaPlayer在Started状态与Paused状态之间切换是异步的。播放音频流的时候,这个转换过程可能会须要几秒钟。
MediaPlayer暂停时,start()方法能够从暂停的位置继续播放。成功调用start方法后会进入Started状态。
处于Paused状态时,调用pause()方法没有效果。
调用stop()方法让MediaPlayer从Started, Paused, Prepared 或 PlaybackCompleted 状态进入 Stopped 状态。
在Stopped状态时,必须先调用prepare() 或 prepareAsync()进入Prepared状态后,才能播放音频。
处于Stopped状态时,调用stop()方法没有效果。
调用seekTo(long, int)
来调整播放位置。
seekTo(long, int)
是一个异步方法,虽然它能马上返回,但实际的位置调整可能会消耗一段时间,特别是在播放音频流的时候。当实际播放位置调整后,内部播放器会回调开发者设置的OnSeekComplete.onSeekComplete()。
在Prepared, Paused 和 PlaybackCompleted状态中,均可以调用seekTo方法。
能够经过getCurrentPosition()
方法来获取当前播放位置。开发者能够得知当前播放的进度等等。
以前使用seekTo
常常遇到恢复播放时位置不许的问题,并且甚至有重头开始播放的现象。
这个是由于seekTo
是回到上一时间点附近的关键帧致使的。
针对这个问题,在最新的Android 8.0平台上,已经有了新的解决方案:seekTo(long msec, @SeekMode int mode)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mPlayer.seekTo(progress, MediaPlayer.SEEK_CLOSEST); } else { mPlayer.seekTo(progress); }
上文的MediaPlayer.SEEK_CLOSEST
,表示找到给定时刻的最接近的一帧(frame)。而不用去找关键帧(key frame)。
音频播放完成后,播放完毕。
若是调用setLooping(boolean)为true,MediaPlayer会停留在Started状态。
若是setLooping为false,内部播放器会调用开发者设置的OnCompletion.onCompletion(),而且进入PlaybackCompleted状态。
处于PlaybackCompleted状态时,调用start()方法能够从头开始播放音频。
开发者能够设置一些监听器,监听MediaPlayer的状态,错误事件等等。开发者应在同一个线程中建立MediaPlayer与设置的监听器。
setOnPreparedListener(android.media.MediaPlayer.OnPreparedListener)
监听MediaPlayer准备完成。通常与prepareAsync
配合使用。
setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener)
获知video大小或video大小改变时的监听。
setOnSeekCompleteListener(android.media.MediaPlayer.OnSeekCompleteListener)
监听调整位置完成。
setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener)
播放完成。
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mediaPlayer) { // 当前播放完毕 } });
setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener)
监听缓冲进度。在播放网络音频时经常使用。
缓冲监听器OnBufferingUpdateListener
mMediaPlayer.prepareAsync(); mMediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() { @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { // 例如在这里更新UI } });
setOnInfoListener(android.media.MediaPlayer.OnInfoListener)
监听普通讯息或者警告信息。
setOnErrorListener(android.media.MediaPlayer.OnErrorListener)
监听错误信息。错误发生时,能够在这里处理错误。
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mediaPlayer, int i, int i1) { LogUtil.e(TAG_PREFIX + " onERR i = " + i + " i1 = " + i1); return true; // 返回true表示在此处理错误,不会回调onCompletion } });
注意onError的返回值。能够选择本身处理error。
* @return True if the method handled the error, false if it didn't. * Returning false, or not having an OnErrorListener at all, will * cause the OnCompletionListener to be called. */ boolean onError(MediaPlayer mp, int what, int extra);
错误代码-38
。一般表示状态不对。在错误的状态执行了错误的操做。
能够参考Android MediaPlayer Error (-38,0) - stackoverflow
播放网络音频时须要Manifest.permission.INTERNET
权限。