1、MediaPlayer的生命周期
有两种类型的弧,单箭头表示的是同步方法调用,有两个箭头代表异步方法调用
1、Idle 状态及End 状态
当使用new()方法创建一个MediaPlayer对象或者调用了其reset()方法时,该MediaPlayer对象处于idle状态。release()方法调用后进入End状态。
2、Initialized 状态
setDataSource()调用之后,MediaPlayer就进入Initialized状态,表示要播放的文件已经设置好了。如果setDataSource()在MediaPlayer的非Idle 状态调用,会抛出IllegalStateException异常
3、Prepared状态
MediaPlayer准备好了
- prepare():同步方法,调用这个方法,MediaPlayer就进入Prepared状态
- prepareAsync():异步方法,经过Preparing 状态,在准备工作结束之后,进入Prepared状态,会回调OnPreparedListener.onPrepared()。
在Prepared状态之后,就可以调用MediaPlayer的一些set方法了,如,setScreenOnWhilePlaying()、setVolume()、setLooping()。
4、Started 状态,Paused 状态
在MediaPlayer准备好之后,调用start()方法,此时MediaPlayer就处于Started状态;调用pause()方法,MediaPlayer进入Paused状态。
如果注册过OnBufferingUpdateListener,在Started 状态时,MediaPlayer就会回调onBufferingUpdate()方法,可以使应用程序保持跟踪视频流的缓冲
5、Stopped 状态
当MediaPlayer处于Started 、Paused 、Prepared、PlaybackCompleted状态下,均可调用stop()停止MediaPlayer,而处于Stopped状态的MediaPlayer要想重新播放,需要通过prepareAsync()(prepare())方法回到Prepared状态才可以。
6、PlaybackCompleted状态
文件正常播放完毕,而又没有设置循环播放的话就进入该状态,并会触发OnCompletionListener的onCompletion()方法。
7、Error 状态
如果由于某种原因MediaPlayer出现了错误,会触发OnErrorListener.onError()事件,此时MediaPlayer即进入Error状态
注意:
1、MediaPlayer会消耗系统的资源,需要在适当的时候把不需要的资源释放掉。例如,如果在Activity中调用onStop()方法时,需要释放MediaPlayer。当Activity恢复或者重新启动时,需要创建一个新的MediaPlayer并且重新准备。
释放MediaPlayer
mediaPlayer.release();
mediaPlayer = null;
2、SurfaceView
SurfaceView是配合MediaPlayer,将一帧一帧的图像,显示在界面上。
指定MediaPlayer显示图像
void setDisplay(SurfaceHolder holder)
- SurfaceHolder可以理解为SurfaceView装载需要显示的一帧帧图像的容器,它可以通过SurfaceHolder.getHolder()方法获得。
- SurfaceView,通过双缓冲的机制来显示帧图像。双缓冲可以理解为有两个线程轮番去 解析视频流的帧图像,当一个线程解析完帧图像后,把图像渲染到界面中,同时另一线程开始解析下一帧图像,使得两个线程轮番配合去解析视频流,以达到流畅播放的效果。
- SurfaceView内部实现了双缓冲的机制,但是实现这个功能是非常消耗系统内存的。因为移动设备的局限性,Android在设计的时候规 定,SurfaceView如果为用户可见的时候,创建SurfaceView的SurfaceHolder用于显示视频流解析的帧图片,如果发现 SurfaceView变为用户不可见的时候,则立即销毁SurfaceView的SurfaceHolder,以达到节约系统资源的目的。所以,需要去维护一个SurfaceHolder,需要用到它的一个回调,SurfaceHolder.Callback()
public interface Callback {
public void surfaceCreated(SurfaceHolder holder);
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height);
public void surfaceDestroyed(SurfaceHolder holder);
}
3、使用
/**
* todo public
* 加载视频
*/
public void loadAndPlay(String videoUrl){
this.videoUrl=videoUrl;
surfaceView.getHolder().setKeepScreenOn(true);
surfaceView.getHolder().addCallback(new SurfaceViewCallback());
}
/**
* 当用户不可见的时候 ,SurfaceHolder 就会销毁,当再次可见的时候,会创建新的SurfaceHolder;
* 使用SurfaceHolder.Callback()维护一个SurfaceHolder
*/
class SurfaceViewCallback implements SurfaceHolder.Callback {
@Override
public void surfaceCreated(SurfaceHolder holder) {
if(mediaPlayer==null){
mediaPlayer=new MediaPlayer();
mediaPlayer.setDataSource(videoUrl);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnCompletionListener(new MediaPlayerCompletion());
mediaPlayer.prepareAsync();
mediaPlayer.setDisplay(surfaceView.getHolder());
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isVideoPrepared=false;
if(mediaPlayer!=null){
mediaPlayer.release();
mediaPlayer=null;
}
}
}
/**
* 视频准备好回调
*/
@Override
public void onPrepared(MediaPlayer mp) {
isVideoPrepared=true;
mediaPlayer.start();
mediaPlayer.setScreenOnWhilePlaying(true);
}
private boolean isVideoPrepared;
Android Multimedia框架总结(一)MediaPlayer介绍之状态图及生命周期、Android Multimedia框架总结(三)MediaPlayer中创建到setDataSource过程