欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android 中MediaPlayer使用详解

程序员文章站 2022-07-07 11:22:50
...

1 介绍

MediaPlayer类是Android开发中用于控制音频/视频文件和流的播放。
下图显示了MediaPlayer对象的生命周期和状态。 椭圆表示MediaPlayer对象可能驻留的状态。弧表示驱动对象状态转换的回放控制操作。 有两种类型的弧线。 带有单箭头的弧表示同步方法调用,而带有双箭头的则表示异步方法调用。
Android 中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提供的方法:

Android 中MediaPlayer使用详解

2 案列一-音乐播放

Android 中MediaPlayer使用详解

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

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 案列二-视频播放

Android 中MediaPlayer使用详解

源码如下:

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();
                }
            }
        });
    }
}

本文代码