Android MediaPlayer的状态管理

用过MediaPlayer来实现音视频播放功能的同学应该都知道MediaPlayer是基于状态的,也就是说MediaPlayer内部保留着一个状态机,用于管理内部的状态。在编写代码时必须始终注意,某些操作仅在播放器处于特定状态时才有效。如果在错误的状态下执行某项操作,则系统可能会抛出异常或导致其他不良行为。

下图展示了android MediaPlayer 的所有状态以及状态之间的转换,该图说明了从哪些状态可以通过调用哪些方法从而将MediaPlayer变为另一种状态。图中,蓝色的椭圆表示MediaPlayer对象可能驻留的状态。弧线表示驱动MediaPlayer对象状态转换的播放控制操作(也即可以调用的方法)。有两种类型的弧线,具有单箭头的弧表示同步方法调用,而具有双箭头的弧表示异步方法调用。


MediaPlayer状态转换图

下面通过文字来对上图的状态变化做一些说明:

  • 当一个MediaPlaye对象刚刚通过 new 方法创建,或者在调用reset()
    方法之后,它将处于Idle状态。并且在 release()方法被调用之后,它将处于End状态。在这两种状态之间是MediaPlayer对象的完整生命周期。

  • 虽然通过new方法新创建的MediaPlayer对象和调用reset()方法之后的MediaPlayer对象都处于Idle状态,但二者之间却存在着细微又非常重要的区别:

    • 首先在两种Idle状态下调用getCurrentPosition(), getDuration(), getVideoHeight(),getVideoWidth(), setAudioAttributes(android.media.AudioAttributes),setLooping(boolean), setVolume(float, float), pause(), start(), stop(), seekTo(long, int), prepare()或prepareAsync()
      等方法都将引起程序上的错误。
    • 其次当上述方法是在通过new新创建的MediaPlayer对象上调用时,即便用户设置了OnErrorListener.onError()回调,onError()方法也不会被调用,并且MediaPlayer的 状态保持不变。相反,当上述方法是在reset()方法之后调用,那么用户将收到OnErrorListener.onError()回调,并且MediaPlayer的状态将变为Error态。
  • 在Idle状态下通过调用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将跳转到Initialized状态。

  • 然后,必须使用 prepare() 或 prepareAsync() 方法完成准备工作。当 准备就绪后,便会进入Prepared状态,

  • 进入Prepared状态后可以通过调用 start() 使其播放媒体内容。start()成功返回后 ,MediaPlayer对象处于Started状态。可以通过调用isPlaying()来测试MediaPlayer对象是否处于Started状态。

  • 可以通过调用 start()、pause()和 seekTo() 等方法在StartedPausedPlaybackCompleted状态之间切换。

  • 调用stop()方法可以让MediaPlayer停止播放,并使处于StartedPausedPrepared PlaybackCompleted状态的MediaPlayer 进入 Stopped状态。不过请注意,当调用 stop()时,除非再次调用prepare()或prepareAsync()将MediaPlayer对象设置为Prepared状态,否则将无法再次调用 start()进行播放。此外调用stop()对已经处于Stopped状态的MediaPlayer对象无效。

  • 当播放到达音视频流的结尾时,播放完成。此时 如果循环模式isLooping 为true 那么MediaPlayer对象将保持在Started状态并将从头开始重新播放。如果isLooping 为false并且预先注册了OnCompletionListener,则播放器引擎将调用用户提供的回调方法OnCompletion.onCompletion()。回调的调用表明该对象现在处于PlaybackCompleted状态。PlaybackCompleted 状态下,再次调用start()可以从音频/视频源的开头重新开始播放。

在编写与 MediaPlayer相关的代码时,请始终牢记该状态图,因为从错误的状态调用其方法是导致错误发生的常见原因。

最后附上MediaPlayer各接口方法的调用时机:

方法名称           有效状态 无效状态 说明
attachAuxEffect {Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} {Idle, Error} 此方法必须在setDataSource之后调用。调用它不会更改对象状态
getCurrentPosition {Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} {Error} 在有效状态下成功调用此方法不会更改状态。在无效状态下调用此方法会将对象转移到错误状态
getDuration {Prepared, Started, Paused, Stopped, PlaybackCompleted} {Idle, Initialized, Error} 在有效状态下成功调用此方法不会更改状态。在无效状态下调用此方法会将对象转移到错误状态
getVideoHeight {Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} {Error} 在有效状态下成功调用此方法不会更改状态。在无效状态下调用此方法会将对象转移到错误状态
getVideoWidth {Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} {Error} 在有效状态下成功调用此方法不会更改状态。在无效状态下调用此方法会将对象转移到错误状态
isPlaying {Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} {Error} 在有效状态下成功调用此方法不会更改状态。在无效状态下调用此方法会将对象转移到错误状态
pause {Started, Paused, PlaybackCompleted} {Idle, Initialized, Prepared, Stopped, Error} 在有效状态下成功调用此方法会将对象转移到“ 暂停”状态。在无效状态下调用此方法会将对象转移到错误状态
prepare {Initialized, Stopped} {Idle, Prepared, Started, Paused, PlaybackCompleted, Error} 在有效状态下成功调用此方法会将对象转移到Prepared状态。在无效状态下调用此方法将引发IllegalStateException
prepareAsync {Initialized, Stopped} {Idle, Prepared, Started, Paused, PlaybackCompleted, Error} 在有效状态下成功调用此方法会将对象转移到“ 准备”状态。在无效状态下调用此方法将引发IllegalStateException
release Any release()之后该对象不再可用
reset {Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted, Error} {} reset之后,对象就像刚刚创建
seekTo {Prepared, Started, Paused, PlaybackCompleted} {Idle, Initialized, Stopped, Error} 在有效状态下成功调用此方法不会更改状态。在无效状态下调用此方法会将对象转移到错误状态
setDataSource {Idle} {Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted, Error} 在有效状态下成功调用此方法会将对象转移到初始化状态。在无效状态下调用此方法将引发IllegalStateException
setLooping {Idle, Initialized, Stopped, Prepared, Started, Paused, PlaybackCompleted} {Error} 在有效状态下成功调用此方法不会更改状态。在无效状态下调用此方法会将对象转移到错误状态
setVolume {Idle, Initialized, Stopped, Prepared, Started, Paused, PlaybackCompleted} {Error} 成功调用此方法不会更改状态
start {Prepared, Started, Paused, PlaybackCompleted} {Idle, Initialized, Stopped, Error} 在有效状态下成功调用此方法会将对象转移到“ 开始”状态。在无效状态下调用此方法会将对象转移到错误状态
stop {Prepared, Started, Stopped, Paused, PlaybackCompleted} {Idle, Initialized, Error} 在有效状态下成功调用此方法会将对象转移到“已停止”状态。在无效状态下调用此方法会将对象转移到错误状态

你可能感兴趣的:(Android MediaPlayer的状态管理)