一、生命状态图
上图显示了MediaPlayer的生命周期和状态,单箭头的弧表示同步方法的调用,双箭头表示异步方法的调用。
二、生命周期详解
1、Idle状态
当MediaPlayer对象使用new创建了一个实例或者调用了reset()方法时,就进入了Idle状态。
2、End状态
在进入Idle状态后调用release()方法,就会变成End状态
在Idle和End状态之间是MediaPlayer的生命周期
3、Error状态
在新创建一个Mediaplayer和在调用reset()方法后有一个不易察觉但非常重要的不同,即在这两种处于Idle状态下调用如下方法getCurrentPosition(),getDuration(),getVideoHeight(),getVideoWidth(),setAudioAttributes(AudioAttributes),setLooping(boolean),setVolume(float, float),pause(),start(),stop(),seekTo(long, int),prepare(),prepareAsync()会出现编程错误。如果实在构建完MediaPlayer对象后立即调用这些方法,则不会调用OnErrorListener.onError()方法,MediaPlayer将扔处于Idle状态;如果是在reset()之后调用这些方法时,则会触发OnErrorListener.onError()方法,MediaPlayer将切换到Error状态。
一般来说,一些播放控制操作可能会由于各种各样的原因失败,比如不支持的音视频格式,音视频分辨率过高,或者输入数据流超时。当需要找到播放器处于Error原因时,需要用户实现调用setOnErrorListener注册OnErrorListener,将会返回错误信息。为了重新使用MediaPlayer,需调用reset()方法,使MediaPlayer恢复到Idle状态。
在非法的状态下调用prepare(),prepareAsync()或者重载setDataSource()方法都会引起IllegalStateException
4、End状态
在不使用MediaPlayer时建议立即调用release()方法一遍MediaPlayer资源得到合理的释放。如果未能调用release()方法,可能会导致MediaPlayer对象初始化失败。在release()方法后MediaPlayer进入到End的状态,一旦MediaPlayer进入到End状态,他将不能再被使用,而且不能再回到MediaPLayer的其他状态。此时MediaPlayer的生命周期终止。
注意,使用new创建的MediaPlayer对象处于Idle状态,但是使用create方法创建的则不处于Idle的状态,如果使用create方法创建成功则直接处于Prepared状态
5、Initialized状态
当调用setDataSource(FileDescriptor)}, setDataSource(String), setDataSource(Context, Uri) ,setDataSource(FileDescriptor, long, long)或者setDataSource(MediaDataSource)任意一个方法时,将会使MediaPlayer的Idle状态变为Initialized状态。如果在非Idle状态下调用setDataSource方法,就会抛出IllegalStateException异常。所以当重载setDataSource方法时,需要抛出IllegalStateException和IOException异常。
6、Prepared状态
MediaPlayer只有在进入Prepared状态后才能开始播放.有两种方式可以使MediaPlayer进入Prepared状态:一种是调用同步方法prepared(),一旦该方法调用返回MediaPlayer对象就会进入Prepared状态。另一种是调用异步方法prepareAsync(),该方法调用返回后首先会将MediaPlayer对象转换成Preparing状态,同时内部播放器继续做其他的工作,直到准备工作完成。如果用户预先调用了setonPreparedListener注册了onPreparedListener函数,当准备工作完成后或者调用Prepared()返回后内部播放器将回调onPreparedListener中的onPrepared回调函数。
注意:Preparing是暂时状态,在非Initialized状态下调用prepared()或者prepareAsync()会抛出IllegalStateException异常。在Prepared状态下,音视频的volume、screenOnWhilePlaying、looping属性,可以通过调用相应的set方法进行调整。
7、Started状态
在播放控制开始之前,必须调用start()方法。当start()方法成功返回时,MediaPlayer的状态由Prepared状态转换成Started状态。可以通过调用isPlaying()方法判断是否处于Started状态。如果用户事先注册了setOnBufferingUpdateListener,播放器内部会回调OnBufferingUpdateListener.onBufferingUpdate()。这个回调主要是使应用程序保持跟踪音视频的buffering状态。
注意:如果MediaPlayer已经处于Started状态,再调用start()是没有任何作用的。
8、Paused状态
MediaPlayer可以暂停和停止播放,并且可以调整当前的播放位置。当调用pause()方法并且回调成功时,MediaPlayer进入Paused状态。需要注意的是:MediaPlayer从Started状态到Paused状态和从Paused状态到Started状态都是异步进行的,也就是说在调用isPlaying()查询状态时需要注意这一点。调用start()去恢复处于Paused状态的MediaPlayer时,当start函数成功返回时将从暂停的位置重新开始播放。MediaPlayer将由Paused变为Started状态。如果MediaPlayer处于Paused状态,再次调用pause()将没有任何作用。
9、Stopped状态
当MediaPlayer处于Started,Paused,Prepared或者PlaybackCompleted状态时,调用stopped()后,MediaPlayer将进入Stopped状态。一旦MediaPlayer处于Stopped状态,直到用户重新调用prepareAsync()或者prepared()且处于Prepared状态后,播放才能重新开始。如果MediaPlayer已经处于了Stopped状态,再调用stop()是没有任何作用的,将保持Stopped的状态。
10、PlaybackCompleted状态
当播放到数据流的末尾时,播放结束。如果setLooping(boolean)被设置为true,则循环播放,MediaPlayer依然处于Started状态。如果setLooping(boolean)被设置为false,如果用户提前注册了setOnCompletionListener监听,则会回调函数OnCompletion.onCompletion(),此时MediaPlayer会进入到PlaybackCompleted状态。当MediaPlayer处于PlaybackCompleted状态时,调用start()函数可以重新开始音视频的播放。
快进和后退
可以通过调用seekTo(long, int)方法,调整当前的播放位置。虽然异步方法seekTo(long, int)可以立即返回,但是调整播放时间的工作还是需要一些时间,特别是当音视频正在缓冲时。如果mediaPlayer实现注册了setOnSeekCompleteListener,在seek操作结束后播放器会回调OnSeekComplete.onSeekComplete()。需要注意的是seekTo(long, int)方法不仅可以在Started状态下调用还可以在Prepared,Paused或者PlaybackCompleted状态下调用。
除此之外可以通过getCurrentPosition()方法,获取当前的播放进度。