Android SurfaceView+MediaPlayer制作播放暂停停止

版权声明:本文为博主原创文章,可以随意转载,但是必须在开头标明出处。 https://blog.csdn.net/qq_29951983/article/details/79188905

这个播放的坑实在是多
其实网上也有很多这种demo,但是我运行之后,点击home,在回到应用发现播放异常,针对此现象,才决定写这篇文章,并重写了一个demo。
得自己控制好视频播放的声明周期
先上一张官网的生命周期图片
这里写图片描述
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

猜你喜欢

转载自blog.csdn.net/qq_29951983/article/details/79188905
今日推荐