[置顶] Android SurfaceView+MediaPlayer制作播放暂停停止
2018-01-28 21:23
567 查看
这个播放的坑实在是多
得自己控制好视频播放的声明周期
先上一张官网的生命周期图片
1、带单箭头的表示同步方法,双箭头的代表异步方法。
2、当一个MediaPlayer对象刚刚使用new创建,或者reset()被调用后,它处于Idle状态; 在release()被调用后,它处于End状态。
3、reset()方法调用后,新构造的MediaPlayer对象和MediaPlayer对象之间存在细微但重要的区别。
getCurrentPosition(), getDuration(), getVideoHeight(), getVideoWidth(), setAudioAttributes(AudioAttributes), setLooping(boolean), setVolume(float, float), pause(), start(), stop(), seekTo(long, int), prepare() or prepareAsync()
在空闲状态下(Idle),对于一个新的MediaPlayer构造对象,如果您监听了OnErrorListener.onError()方法,那么将不会回调。但是对于一个调用了reset方法后的MediaPlayer对象(此时也属于Idle状态),当调用了上面的方法,就会在OnErrorListener.onError()方法回调。
4、一旦MediaPlayer对象不再被使用,立即调用release(),一旦MediaPlayer对象处于End状态,就不能再使用它,并且无法将其返回到任何其他状态。
每当应用程序的Activity暂停(onPause()或停止它的onStop()方法被调用)时,应该调用此方法来释放MediaPlayer对象。
5、使用new MediaPlayer时,处于Idle状态,而使用create这个重载方法创建对象时,直接处于Prepared状态。
6、可以创建setOnErrorListener监听观察播放期间的一切错误。
7、当进入错误状态可以调用reset,重新回复Idle状态。
8、调用
setDataSource(FileDescriptor)或setDataSource(String)或setDataSource(Context,Uri)或setDataSource(FileDescriptor,long,long)setDataSource(MediaDataSource)
将处于空闲状态的MediaPlayer对象转换为Initialized状态。
如果在任何其他状态下调用setDataSource(),则会引发IllegalStateException。
9、可以调用prepare()方法,该方法返回就进入Prepared状态。或者调用prepareAsync,当设置了setOnPreparedListener监听时,在onPrepared方法中,会将MediaPlayer置为Prepared状态。
10、要开始播放,必须调用start()。在start()成功返回后,MediaPlayer对象处于Started状态。可以调用isPlaying()来测试MediaPlayer对象是否处于Started状态。
调用start()对已处于Started状态的MediaPlayer对象没有影响。
调用start()以恢复已暂停的MediaPlayer对象的播放,并且恢复的播放位置与暂停的位置相同。当对start()的调用返回时,暂停的MediaPlayer对象将返回到Started状态。
11、可以调用pause()方法对处于paused状态
调用pause()对已处于暂停状态的MediaPlayer对象没有影响。
12、调用stop()将停止播放,并使MediaPlayer处于Started,Paused,Prepared或PlaybackCompleted状态进入停止状态。
一旦处于停止状态,直到调用prepare()或prepareAsync()以再次将MediaPlayer对象设置为Prepared状态,才能开始播放。
13、seekTo(long,int)可以在其他状态下调用,例如Prepared,Paused和PlaybackCompleted状态。当在这些状态下调用seekTo(long,int)时,如果流有视频并且请求的位置有效,则将显示一个视频帧。
此外,可以通过调用getCurrentPosition()来获取当前的实际播放位置,这对于需要跟踪播放进度的音乐播放器等应用程序很有帮助。
14、当播放到达流尾时,播放完成。
如果使用setLooping(boolean)将循环模式设置为true,则MediaPlayer对象应保持在Started状态。
如果循环模式设置为false,则如果通过setOnCompletionListener(OnCompletionListener)事先注册了OnCompletionListener,则播放器引擎将调用用户提供的回调方法OnCompletion.onCompletion()。调用回调信号表明对象现在处于PlaybackCompleted状态。
在PlaybackCompleted状态下,调用start()可以从音频/视频源的开头重新开始播放。
其实网上也有很多这种demo,但是我运行之后,点击home,在回到应用发现播放异常,针对此现象,才决定写这篇文章。
Demo:
主要还是要控制好状态,因为MediaPlayer的底层是jni,如果当前的MediaPlayer状态和jni的状态不一致就会引发IllegalArgumentException这个异常。
这里在我的魅蓝闹腾5发现个问题(也不知道是我自己手机问题还是咋的),就是在我暂停(MediaPlayer调用pause())之后,按Home键,到手机主页。然后我应用Activity在切换回来,然后播放,发现无法播放。
最后发现状态不对,但是我已经按照官网给的声明周期去编写的代码。
最后解决办法,在surfaceCreated方法中放了一个大的try,如果状态不对,那就重新更新状态呗。在catch中将MediaPlayer调用release,并且赋值为null。然后恢复上次播放的点,虽然解决了,但是我总觉得有更好的方法,如果你知道,你可以加我低下的QQ群。
参考连接:Android官网
视频参考连接
微信公众号:
QQ群:365473065
得自己控制好视频播放的声明周期
先上一张官网的生命周期图片
1、带单箭头的表示同步方法,双箭头的代表异步方法。
2、当一个MediaPlayer对象刚刚使用new创建,或者reset()被调用后,它处于Idle状态; 在release()被调用后,它处于End状态。
3、reset()方法调用后,新构造的MediaPlayer对象和MediaPlayer对象之间存在细微但重要的区别。
getCurrentPosition(), getDuration(), getVideoHeight(), getVideoWidth(), setAudioAttributes(AudioAttributes), setLooping(boolean), setVolume(float, float), pause(), start(), stop(), seekTo(long, int), prepare() or prepareAsync()
在空闲状态下(Idle),对于一个新的MediaPlayer构造对象,如果您监听了OnErrorListener.onError()方法,那么将不会回调。但是对于一个调用了reset方法后的MediaPlayer对象(此时也属于Idle状态),当调用了上面的方法,就会在OnErrorListener.onError()方法回调。
4、一旦MediaPlayer对象不再被使用,立即调用release(),一旦MediaPlayer对象处于End状态,就不能再使用它,并且无法将其返回到任何其他状态。
每当应用程序的Activity暂停(onPause()或停止它的onStop()方法被调用)时,应该调用此方法来释放MediaPlayer对象。
5、使用new MediaPlayer时,处于Idle状态,而使用create这个重载方法创建对象时,直接处于Prepared状态。
6、可以创建setOnErrorListener监听观察播放期间的一切错误。
7、当进入错误状态可以调用reset,重新回复Idle状态。
8、调用
setDataSource(FileDescriptor)或setDataSource(String)或setDataSource(Context,Uri)或setDataSource(FileDescriptor,long,long)setDataSource(MediaDataSource)
将处于空闲状态的MediaPlayer对象转换为Initialized状态。
如果在任何其他状态下调用setDataSource(),则会引发IllegalStateException。
9、可以调用prepare()方法,该方法返回就进入Prepared状态。或者调用prepareAsync,当设置了setOnPreparedListener监听时,在onPrepared方法中,会将MediaPlayer置为Prepared状态。
10、要开始播放,必须调用start()。在start()成功返回后,MediaPlayer对象处于Started状态。可以调用isPlaying()来测试MediaPlayer对象是否处于Started状态。
调用start()对已处于Started状态的MediaPlayer对象没有影响。
调用start()以恢复已暂停的MediaPlayer对象的播放,并且恢复的播放位置与暂停的位置相同。当对start()的调用返回时,暂停的MediaPlayer对象将返回到Started状态。
11、可以调用pause()方法对处于paused状态
调用pause()对已处于暂停状态的MediaPlayer对象没有影响。
12、调用stop()将停止播放,并使MediaPlayer处于Started,Paused,Prepared或PlaybackCompleted状态进入停止状态。
一旦处于停止状态,直到调用prepare()或prepareAsync()以再次将MediaPlayer对象设置为Prepared状态,才能开始播放。
13、seekTo(long,int)可以在其他状态下调用,例如Prepared,Paused和PlaybackCompleted状态。当在这些状态下调用seekTo(long,int)时,如果流有视频并且请求的位置有效,则将显示一个视频帧。
此外,可以通过调用getCurrentPosition()来获取当前的实际播放位置,这对于需要跟踪播放进度的音乐播放器等应用程序很有帮助。
14、当播放到达流尾时,播放完成。
如果使用setLooping(boolean)将循环模式设置为true,则MediaPlayer对象应保持在Started状态。
如果循环模式设置为false,则如果通过setOnCompletionListener(OnCompletionListener)事先注册了OnCompletionListener,则播放器引擎将调用用户提供的回调方法OnCompletion.onCompletion()。调用回调信号表明对象现在处于PlaybackCompleted状态。
在PlaybackCompleted状态下,调用start()可以从音频/视频源的开头重新开始播放。
其实网上也有很多这种demo,但是我运行之后,点击home,在回到应用发现播放异常,针对此现象,才决定写这篇文章。
Demo:
import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.Button; import java.io.IOException; public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener, MediaPlayer.OnInfoListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnVideoSizeChangedListener, MediaPlayer.OnSeekCompleteListener, View.OnClickListener, MediaPlayer.OnBufferingUpdateListener { private SurfaceView surfaceView; private SurfaceHolder surfaceHolder; private MediaPlayer player; private Button mStart; private Button mPause; private Button mStop; private final static int IDLE = 0; private final static int INITIALZED = 1; private final static int PREPARED = 2; private final static int STARTED = 3; private final static int COMPLETED = 4; private final static int PREPARING = 5; private final static int STOP = 6; private final static int PAUSE = 7; private final static int ERROR = 8; private final static int RELEASE = 9; private static int CURRENT = -1; private int position = 0; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mStart = findViewById(R.id.start); mPause = findViewById(R.id.pause); mStop = findViewById(R.id.stop); mStart.setOnClickListener(this); mPause.setOnClickListener(this); mStop.setOnClickListener(this); surfaceView = findViewById(R.id.SurfaceView01); surfaceHolder = surfaceView.getHolder(); surfaceHolder.addCallback(this); player = new MediaPlayer(); player.setLooping(true); CURRENT = IDLE; setAttrs(player); } @Override public void surfaceCreated(SurfaceHolder holder) { try { surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); player.setAudioStreamType(AudioManager.STREAM_MUSIC); surfaceHolder.setFixedSize(320, 220); opertion(IDLE); CURRENT = INITIALZED; player.setDataSource("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"); CURRENT = PREPARING; player.prepareAsync(); } catch (Exception e) { player.release(); player = null; player = new MediaPlayer(); setAttrs(player); player.setLooping(true); opertion(IDLE); try { CURRENT = INITIALZED; player.setDataSource("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"); } catch (IOException e1) { e1.printStackTrace(); } CURRENT = PREPARING; player.prepareAsync(); } } public void setAttrs(MediaPlayer player) { System.out.println("XXXMainActivity.setAttrs"); player.setOnCompletionListener(this); player.setOnErrorListener(this); player.setOnInfoListener(this); player.setOnPreparedListener(this); player.setOnSeekCompleteListener(this); player.setOnVideoSizeChangedListener(this); player.setOnBufferingUpdateListener(this); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } @Override public void onCompletion(MediaPlayer mp) { CURRENT = COMPLETED; } @Override public boolean onError(MediaPlayer mp, int what, int extra) { opertion(ERROR); return false; } @Override public boolean onInfo(MediaPlayer mp, int what, int extra) { return false; } @Override public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { } @Override public void onSeekComplete(MediaPlayer mp) { } @Override public void onPrepared(MediaPlayer mp) { player.setDisplay(surfaceHolder); CURRENT = PREPARED; player.start(); if (position != 0) { player.seekTo(position); } System.out.println("XXXXonPrepared+ " + Thread.currentThread().getName()); CURRENT = STARTED; } public void opertion(int current) { switch (current) { case IDLE: CURRENT = IDLE; break; case INITIALZED: break; case PREPARED: break; case STARTED: if (CURRENT == PAUSE) { player.start(); CURRENT = STARTED; } else if (CURRENT == STOP) { CURRENT = PREPARING; player.prepareAsync(); } break; case COMPLETED: break; case PREPARING: break; case STOP: if (CURRENT == STOP) { return; } if (CURRENT == STARTED || CURRENT == PAUSE || CURRENT == PREPARED || CURRENT == COMPLETED) { player.stop(); CURRENT = STOP; position = 0; } break; case PAUSE: if (CURRENT == PAUSE) { return; } if ((CURRENT == STARTED) || (player.isLooping() && CURRENT == COMPLETED)) { player.pause(); CURRENT = PAUSE; } break; case ERROR: player.reset(); opertion(IDLE); player.release(); player = new MediaPlayer(); setAttrs(player); player.setLooping(true); CURRENT = IDLE; try { CURRENT = INITIALZED; player.setDataSource("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"); } catch (IOException e1) { e1.printStackTrace(); } player.setAudioStreamType(AudioManager.STREAM_MUSIC); surfaceHolder.setFixedSize(320, 220); surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); CURRENT = PREPARING; player.prepareAsync(); break; case RELEASE: if (player != null) { CURRENT = RELEASE; player.reset(); player.release(); } break; } } @Override public void onClick(View v) { if (v.getId() == R.id.start) { opertion(STARTED); } else if (v.getId() == R.id.pause) { opertion(PAUSE); } else if (v.getId() == R.id.stop) { opertion(STOP); } } @Override protected void onRestart() { super.onRestart(); } @Override protected void onResume() { super.onResume(); } @Override protected void onPause() { position = player.getCurrentPosition(); opertion(PAUSE); super.onPause(); } @Override protected void onStop() { opertion(RELEASE); super.onStop(); } @Override protected void onStart() { super.onStart(); } @Override protected void onDestroy() { super.onDestroy(); } @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/LinearLayout01" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <SurfaceView android:id="@+id/SurfaceView01" android:layout_width="fill_parent" android:layout_height="300dp"></SurfaceView> <LinearLayout android:weightSum="5" android:id="@+id/LinearLayout02" android:layout_width="match_parent" android:layout_height="wrap_content" > <Button android:layout_weight="1" android:id="@+id/start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="播放视频"></Button> <Button android:layout_weight="1" android:text="暂停" android:id="@+id/pause" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:layout_weight="1" android:id="@+id/stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="停止播放"></Button> </LinearLayout> </LinearLayout>
<uses-permission android:name="android.permission.INTERNET" />
主要还是要控制好状态,因为MediaPlayer的底层是jni,如果当前的MediaPlayer状态和jni的状态不一致就会引发IllegalArgumentException这个异常。
这里在我的魅蓝闹腾5发现个问题(也不知道是我自己手机问题还是咋的),就是在我暂停(MediaPlayer调用pause())之后,按Home键,到手机主页。然后我应用Activity在切换回来,然后播放,发现无法播放。
最后发现状态不对,但是我已经按照官网给的声明周期去编写的代码。
最后解决办法,在surfaceCreated方法中放了一个大的try,如果状态不对,那就重新更新状态呗。在catch中将MediaPlayer调用release,并且赋值为null。然后恢复上次播放的点,虽然解决了,但是我总觉得有更好的方法,如果你知道,你可以加我低下的QQ群。
参考连接:Android官网
视频参考连接
微信公众号:
QQ群:365473065
相关文章推荐
- Android中使用surfaceview+MediaPlayer播放视频
- Android两种播放视频的方法(SurfaceView+MediaPlayer+SeekBar)跟(VideoView+MediaController)
- Android 视频播放之MediaPlayer+SurfaceView
- Android SurfaceView + MediaPlayer实现分段视频无缝播放
- Android SurfaceView+ mediaplayer 重复播放视频
- Android SurfaceView+MediaPlayer 播放本地raw文件夹下的视频
- Android MediaPlayer+SurfaceView播放视频 (异常处理)
- SurfaceView+MediaPlayer视频播放并用SharedPreferences实现暂停功能
- Android 使用 MediaPlayer + SurfaceView 播放视频
- Android SurfaceView+MediaPlayer实现视频播放
- Android视频播放框架Vitamio集成及使用MediaPlayer+SurfaceView、VideoView+MediaController实现播放网络视频
- android视频播放(二) 利用android原生的MediaPlayer+SurfaceView
- android开发SurfaceView+MediaPlayer实现视频播放
- Android MediaPlayer+SurfaceView实现视频播放
- Android MediaPlayer详解视屏播放之SurfaceView(四)
- android下面使用SurfaceView+ mediaPlayer播放视频
- android中使用surfaceview+MediaPlayer播放视频
- Android视频播放: SurfaceView+MediaPlayer播放视频(二)
- android播放视频(三)利用原生的MediaPlayer+SurfaceView之提高
- Android - SurfaceView+MediaPlayer、VideoView 2种方法播放视频