您的位置:首页 > 移动开发 > Android开发

[置顶] 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:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: