1 介绍

MediaPlayer类是Android开发中用于控制音频/视频文件和流的播放。

下图显示了MediaPlayer对象的生命周期和状态。 椭圆表示MediaPlayer对象可能驻留的状态。弧表示驱动对象状态转换的回放控制操作。 有两种类型的弧线。 带有单箭头的弧表示同步方法调用,而带有双箭头的则表示异步方法调用。

从上图可以知道MediaPlayer有以下状态:

  • 当一个MediaPlayer对象刚刚使用new创建,或者reset()被调用后,它处于Idle状态(闲置状态); 在release()被调用后,它处于End状态。 这两个状态之间是MediaPlayer对象的生命周期。
    1) 新构造的MediaPlayer对象和在调用reset()方法之后的MediaPlayer对象之间存在细微但重要的区别。在两种情况下,在idle状态下调用诸如getCurrentPosition(), getDuration(), getVideoHeight(), getVideoWidth(), setAudioAttributes(AudioAttributes), setLooping(boolean), setVolume(float, float), pause(), start(), stop(), seekTo(long, int), prepare() 或者prepareAsync() 之类的方法会出现错误。如果在构造MediaPlayer对象后立即调用这些方法中的任何一个,则用户提供的回调方法OnErrorListener.onError()将不会被内部播放器引擎调用,并且对象状态保持不变; 但是如果这些方法在reset()之后被调用,那么用户提供的 OnErrorListener.onError()方法会被内部的播放器引擎调用,并且这个对象将被转移到Error状态。
    2) 建议一旦MediaPlayer对象不再使用,应该立即调用release()方法,以便与MediaPlayer对象关联的内部播放器引擎使用的资源可以立即释放。 资源可能包括硬件加速组件等单一资源。一旦MediaPlayer对象处于End状态,就不能再使用它,并且无法将其返回到任何其他状态。
    3) 此外,使用new创建的MediaPlayer对象处于Idle状态,而使用重载的便捷创建方法之一创建的对象不处于Idle状态。 事实上,如果使用create方法创建成功,则这些对象处于Prepared状态。
  • 一般来说,由于各种原因,如音频/视频格式不支持,音频/视频交织不良,分辨率过高等,某些播放控制操作可能会失败。 因此,在这种情况下,错误报告和恢复是一个重要的问题.在所有这些错误情况下,如果通过setOnErrorListener(android.media.MediaPlayer.OnErrorListener)事先注册了OnErrorListener,则内部播放器引擎将调用用户提供的OnErrorListener.onError()方法。
    1) 需要注意的是,一旦发生错误,即使应用程序未注册错误侦听器,MediaPlayer对象也会进入错误状态。
    2) 为了重用处于“错误”状态的MediaPlayer对象并从错误中恢复,可以调用reset()将对象恢复到其空闲状态。
  • 调用setDataSource(FileDescriptor)或setDataSource(String)或setDataSource(Context,Uri)或setDataSource(FileDescriptor,long,long)或setDataSource(MediaDataSource)将处于空闲状态的MediaPlayer对象转换为Initialized状态。
    1) 如果在任何其他状态下调用setDataSource(),则会引发IllegalStateException。
  • MediaPlayer对象必须先进入准备状态,然后才能开始播放。
    1) 有两种方式(同步和异步),可以达到Prepared状态:调用prepare()(同步),在方法调用返回时将对象转移到Prepared状态,或调用prepareAsync()( 异步),在调用返回之后首先将对象转移到准备状态,而内部播放引擎继续进行准备工作的其余部分,直到准备工作完成。 准备完成或prepare()调用返回时,如果预先通过setOnPreparedListener(android.media.MediaPlayer.OnPreparedListener)注册了OnPreparedListener,则内部播放器引擎将调用用户提供的OnPreparedListener接口的onPrepared()的回调方法。
    2) 处于“准备”状态时,可通过调用相应的设置方法来调整诸如音频/音量,屏幕亮度,循环等属性。
  • 要开始播放,必须调用start()。 在start()成功返回后,MediaPlayer对象处于Started状态。 可以调用isPlaying()来测试MediaPlayer对象是否处于Started状态。
    1) 当处于Started状态时,如果已经通过setOnBufferingUpdateListener(OnBufferingUpdateListener)事先注册了OnBufferingUpdateListener,则内部播放器引擎会调用用户提供的OnBufferingUpdateListener.onBufferingUpdate()回调方法。 此回调允许应用程序在流式传输音频/视频时跟踪缓冲状态。
    2) 调用start()对已处于Started状态的MediaPlayer对象是没有影响的。
  • 可以暂停和停止播放,并可以调整当前的播放位置。 可以通过暂pause()暂停播放。 当对pause()的调用返回时,MediaPlayer对象进入暂停状态。 请注意,从启动状态到暂停状态(反之亦然)在播放引擎中异步发生。 在调用isPlaying()方法更新状态之前可能需要一段时间,对于流式内容,可能需要几秒钟的时间。
    1) 调用start()以恢复已暂停的MediaPlayer对象的播放,并且恢复的播放位置与暂停的位置相同。 当对start()的调用返回时,暂停的MediaPlayer对象将返回到Started状态。
    2) 调用pause()对已处于暂停状态的MediaPlayer对象没有影响。
  • 调用stop()将停止播放,并使MediaPlayer处于已启动,已暂停,准备或播放完成状态以进入已停止状态。
    1) 一旦处于停止状态,直到调用prepare()或prepareAsync()以再次将MediaPlayer对象设置为“准备”状态,才能开始播放。
    2) 调用stop()对已处于停止状态的MediaPlayer对象没有影响。
  • 可以通过调用seekTo(long,int)来调整播放位置。
    1) 尽管seekTo(long,int)异步调用立即返回,但实际的搜索操作可能需要一段时间才能完成,特别是对于正在流式传输的音频/视频。当实际的查找操作完成时,如果已经通过setOnSeekCompleteListener(OnSeekCompleteListener)事先注册了OnSeekCompleteListener,则内部播放器引擎会调用用户提供的OnSeekComplete.onSeekComplete()。
    2) 请注意seekTo(long,int)也可以在其他状态下调用,例如Prepared,Paused和PlaybackCompleted状态。当在这些状态下调用seekTo(long,int)时,如果流有视频并且请求的位置有效,则将显示一个视频帧。
    3) 此外,可以通过调用getCurrentPosition()来获取当前的实际播放位置。
  • 当播放到达流尾时,播放完成。
    1) 如果使用setLooping(boolean)将循环模式设置为true,则MediaPlayer对象应保持在Started状态。
    2) 如果循环模式设置为false,则如果通过setOnCompletionListener(OnCompletionListener)事先注册了OnCompletionListener,则播放器引擎将调用用户提供的回调方法OnCompletion.onCompletion()。调用回调信号表明对象现在处于PlaybackCompleted状态。
    3) 在PlaybackCompleted状态下,调用start()可以从音频/视频源的开头重新开始播放。
  • 如果使用setLooping(boolean)将循环模式设置为true,则MediaPlayer对象应保持在Started状态。
    1) 如果循环模式设置为false,则如果通过setOnCompletionListener(OnCompletionListener)事先注册了OnCompletionListener,则播放器引擎将调用用户提供的回调方法OnCompletion.onCompletion()。 调用回调信号表明对象现在处于PlaybackCompleted状态。
    2) 在PlaybackCompleted状态下,调用start()可以从音频/视频源的开头重新开始播放。

下面是MediaPlay提供的方法:

2 案列一-音乐播放

我们通过混合开启服务方式实现音乐播放案例:

MainActivity代码:

/**
 * //1.播放音乐后台进行   需要在服务中进行  创建服务
 //2.在服务中  bind服务  服务返回  代理人对象
 //3.混合开启服务  start bind    在activity 销毁的时候记得解除绑定
 //4.activity调用服务的方法

 //5.音乐播放的逻辑添加进去
 //6.音乐播放的进度
 //1 添加上一个 sb
 //2在服务中 用player 获取 最大值 和进度
 //3定义一个定时器 每个1秒发送一个 当前的播放位置的  进度  给sb handler
 //4在activity 中  给sb设置最大值和 进度
 //5拖动  需要给sb设置个  sb 发生改变的监听  获取到  拖动的位置
 //6在服务中  添加一个方法  到指定位置播放  seekto
 //7调用  callseek方法设置音乐的  拖动播放的位置
 //8当音乐播放完成的时候  关闭定时器
 */
public class MainActivity extends Activity {

    private static SeekBar sb;
    private MusicService.MyBinder myBinder;
    private MyServiceConnection conn;
    public static Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0:

                    int duration = msg.arg1;
                    int progress = msg.arg2;
                    sb.setMax(duration);
                    sb.setProgress(progress);
                    break;

            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sb = ((SeekBar) findViewById(R.id.sb));
        sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                int progress = seekBar.getProgress();
                myBinder.callSeekTo(progress);
            }
        });
        // 开启服务 绑定服务
        Intent intent = new Intent();
        intent.setClass(this, MusicService.class);
        startService(intent);
        conn = new MyServiceConnection();
        bindService(intent, conn, BIND_AUTO_CREATE);
    }
    //播放
    public void play(View view) {
        myBinder.callPlay(this);
    }
    //暂停
    public void pause(View view) {
        myBinder.callPause();
    }
    //继续播放
    public void replay(View view) {
        myBinder.callResume();

    }
    public class MyServiceConnection implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = (MusicService.MyBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

MusicService代码:

public class MusicService extends Service {
    private String TAG = "MusicService";
    private MediaPlayer player;

    public MusicService() {
    }
    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        player = new MediaPlayer();
    }
    public class MyBinder extends Binder implements MusicInterface{
        @Override
        public void callPlay(Activity activity) {
            play(activity);
        }

        @Override
        public void callPause() {
            pause();
        }

        @Override
        public void callResume() {
            resume();
        }

        @Override
        public void callSeekTo(int position) {
            seekTo(position);
        }
    }

    private void seekTo(int position) {
        Log.e(TAG, "当前位置" + position);
        player.seekTo(position);

    }
    private void resume() {
        Log.e(TAG, "继续播放");
        player.start();
    }

    private void pause() {
        Log.e(TAG, "暂停");
        player.pause();
    }
    private void play(Activity context) {
        Log.e(TAG, "播放");
        player.reset();
        PermisionUtils.verifyStoragePermissions(context);//动态获取权限
        File file = new File(Environment.getExternalStorageDirectory(), "123.mp3");

            if(file.exists()){
                Log.e(TAG, "音乐文件存在");
                try {
                    player.setDataSource(file.getAbsolutePath());
                    player.prepare();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                player.start();
                seekBar();
            }else {
                Log.e(TAG, "音乐文件不存在");
            }
    }

    private void seekBar() {
        final int duration = player.getDuration();

        //定时器
        final Timer timer = new Timer();
        final TimerTask timerTask=new TimerTask() {
            @Override
            public void run() {
                int currentPosition = player.getCurrentPosition();
                Message msg = Message.obtain();
                msg.what = 0;
                msg.arg1 = duration;
                msg.arg2 = currentPosition;
                MainActivity.handler.sendMessage(msg);

            }
        };
        timer.schedule(timerTask, 500, 1000);
        player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                timer.cancel();
                timerTask.cancel();
            }
        });
    }
}

MusicInterface代码:

public interface MusicInterface {
    public void callPlay(Activity activity);
    public void callPause();
    public void callResume();
    public void callSeekTo(int position);

}

PermisionUtils代码:

public class PermisionUtils {
    // Storage Permissions
    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE};

    /**
     * Checks if the app has permission to write to device storage
     * If the app does not has permission then the user will be prompted to
     * grant permissions
     *
     * @param activity
     */
    public static void verifyStoragePermissions(Activity activity) {
        // Check if we have write permission
        int permission = ActivityCompat.checkSelfPermission(activity,
                Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if (permission != PackageManager.PERMISSION_GRANTED) {
            // We don't have permission so prompt the user
            ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,
                    REQUEST_EXTERNAL_STORAGE);
        }
    }
}

3 案列二-视频播放

源码如下:

public class MainActivity extends AppCompatActivity {

    private SurfaceView surfaceView;
    private SurfaceHolder holder;
    private MediaPlayer player;
    private int currentPosition;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        surfaceView = (SurfaceView) findViewById(R.id.sfv);
        holder = surfaceView.getHolder();
        holder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                player = new MediaPlayer();
                try {
                    player.setDataSource("http://10.0.2.2:8080/NetWork/test.mp4");
                    player.setDisplay(holder);
                    player.prepareAsync();
                    player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                        @Override
                        public void onPrepared(MediaPlayer mp) {
                            player.start();
                            if(currentPosition!=0){
                                player.seekTo(currentPosition);
                            }
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }


            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                if (player!=null){
                    currentPosition = player.getCurrentPosition();
                    player.stop();
                    player.reset();
                    player.release();
                }
            }
        });
    }
}