Android MediaPlay的使用以及实现音频播放器

一、MediaPlay状态机详解(MediaPlay的生命周期)
MediaPlayer状态机如下图所示
在这里插入图片描述1、Idle(闲置)状态与End(结束)状态
在这里插入图片描述
MediaPlayer 对象声明周期 : 从 Idle 到 End 状态就是 MediaPlayer 整个生命周期;
生命周期开始 : 进入 Idle (闲置) 状态;
生命周期结束 : 进入 End (结束) 状态;

  • Idle 和 End 状态转换
    进入 Idle 状态 : new MediaPlayer() 或者 任何状态调用了 reset() 方法之后, 进入 Idle (闲置) 状态;
    进入 End 状态 : 在 Idle 状态调用 release() 方法后, 会进入 End (结束) 状态(涉及到资源的释放),不能转换为其他状态;
    注意:create()初始化的MediaPlayer直接进入Prepared状态

2、Error(错误)状态
在这里插入图片描述
Error状态转换:
进入Error状态:检测到异常,系统回调onError()进入Error状态
离开Error状态:可以使用reset()回到Idle状态
注册监听 : 注册一个 OnErrorListener 监听器重写OnError(), 用于获取 播放器引擎 内部发生的错误;
注册方法 : 调用 MediaPlayer.setOnErrorListener(OnErrorListener) 方法, 注册 OnErrorListener;
3、Initialized(初始化)状态
在这里插入图片描述
Initialized 状态转换 : 在 Idle 状态调用 setDataSource() 方法, MediaPlayer 会迁移到 Initialized 状态;
注意 : 只能在 Idle 状态调用该方法, 如果在其它状态调用该方法, 会报出 IllegalStateException 异常;
4、Prepared(就绪)和Preparing(准备中)状态
在这里插入图片描述
Prepared状态转移(两种方式)
Initialized状态 调用 prepared()进入Prepared状态 (同步操作,若数据量较大则容易造成主线程阻塞甚至ANR)
Initialized状态 调用prepareAsync()进入Preparing状态,注册OnPreparedListener.OnPrepared(),在将准备就绪后的操作放置OnPrepared()中(异步操作,便于操纵数据量大的情况,避免主线程阻塞)
5、Started(开始)状态
在这里插入图片描述
Started状态转移:
Prepared状态调用start()进入Started状态
判断MediaPlayer是否在Started状态:isPlaying():boolean
跟踪缓冲状态 : 在 Started 状态, 调用 OnBufferingUpdateListener.onBufferingUpdate() 方法, 可以获取视频音频流的缓冲状态;
6、Paused(暂停)状态
在这里插入图片描述Paused状态转移:
Started状态调用paused()进入Paused状态
Paused状态调用start()进入Started状态
7、Stop状态
在这里插入图片描述Stop状态转移
在 Prepared, Started, Paused, PlaybackCompleted 状态下 调用 stop() 方法, MediaPlayer 会迁移到 Stopped 状态;
注意Stop状态不能直接start(),要回到prepared状态(prepare()或prepareAsyn()),才能start
8、播放位置调整seekTo()
在 Prepared, Started, Paused, PlaybackCompleted 状态下 调用 stop() 方法, MediaPlayer 会迁移到 Stopped 状态;
在这里插入图片描述
seekTo() 方法说明 : 该方法异步, 调用后 播放器引擎还需要进行其它操作, 跳转才能完成;
进行的操作 : 播放器引擎会回调 OnSeekComplete.onSeekComplete()方法, 该方法通过 setOnSeekCompleteListener() 方法注册;
获取播放位置 : 调用 getCurrentPosition() 方法, 可以获取当前播放的位置, 可以帮助播放器更新进度条;
9、PlaybackCompleted (播放完毕) 状态
在这里插入图片描述PlaybackCompleted 状态转移 : 如果设置了循环模式SetLooping(), 那么播放完毕之后会重新进入Started状态;若没设置循环,则调用 OnCompletion.onCompletion() 回调方法, MediaPlayer 会进入 PlaybackCompleted 状态;
也可以在该状态直接调用start()进入Started状态
二、MediaPlayer是Android系统自带的,可以用来播放音频、视频和流媒体。MediaPlayer包含了Audio和Video的播放功能,下面介绍一些常用方法。
常用方法

方法说明
create创建一多媒体
getCurrentPosition当前播放位置
getDuration文件的总时间
getVideoHeight视频的高度
getVideoWidth视频的宽度
isLooping是否循环播放
isPlaying是否正在播放
start开始播放
pause暂停
prepare准备(同步)
prepareAsync准备(异步)
stop停止播放
release释放相关资源
reset重置
seekTo指定
setAudioStreamType设置类型
setDataSource设多媒体数据来源
setDisplay设置显示多媒体的载体
setLooping是否循环播放
setOnButteringUpdateListener网络流媒体的缓冲监听
setOnErrorListener错误信息监听
setOnVideoSizeChangedListener视频尺寸监听
setScreenOnWhilePlaying设置是否保持屏幕常亮
setVolume设置音量
  • void setDataSource(String path) 通过一个具体的路径来设置MediaPlayer的数据源,path可以是本地的一个路径,也可以是一个网络路径
  • void setDataSource(Context context, Uri uri) 通过给定的Uri来设置MediaPlayer的数据源,这里的Uri可以是网络路径或是一个ContentProvider的Uri。
  • void setDataSource(MediaDataSource dataSource) 通过提供的MediaDataSource来设置数据源
  • void setDataSource(FileDescriptor fd) 通过文件描述符FileDescriptor来设置数据源
  • int getCurrentPosition() 获取当前播放的位置
  • int getAudioSessionId() 返回音频的session ID
  • int getDuration() 得到文件的时间
  • TrackInfo[] getTrackInfo() 返回一个track信息的数组
  • boolean isLooping () 是否循环播放
  • boolean isPlaying() 是否正在播放
  • void pause () 暂停
  • void start () 开始
  • void stop () 停止
  • void prepare() 同步的方式装载流媒体文件。
  • void prepareAsync() 异步的方式装载流媒体文件。
  • void reset() 重置MediaPlayer至未初始化状态。
  • void release () 回收流媒体资源。
  • void seekTo(int msec) 指定播放的位置(以毫秒为单位的时间)
  • void setAudioStreamType(int streamtype) 指定流媒体类型
  • void setLooping(boolean looping) 设置是否单曲循环
  • void setNextMediaPlayer(MediaPlayer next) 当 当前这个MediaPlayer播放完毕后,MediaPlayer next开始播放
  • void setWakeMode(Context context, int mode):设置CPU唤醒的状态。
  • setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener) 网络流媒体的缓冲变化时回调
  • setOnCompletionListener(MediaPlayer.OnCompletionListener listener) 网络流媒体播放结束时回调
  • setOnErrorListener(MediaPlayer.OnErrorListener listener) 发生错误时回调
  • setOnPreparedListener(MediaPlayer.OnPreparedListener listener):当装载流媒体完毕的时候回调。
  • setOnInfoListener(OnInfoListener l) 信息监听

三、实现一个音频播放器,效果图如下
在这里插入图片描述
1、我们首先先写一个音频播放器类,该类主要实现了播放、暂停、重新播放、循环播放、停止播放、进度条等功能

public class MyMusicPlayer implements MediaPlayer.OnPreparedListener ,MediaPlayer.OnCompletionListener,MediaPlayer.OnErrorListener,MediaPlayer.OnSeekCompleteListener{

    private static final String TAG = "MyMusicPlayer";
    private MediaPlayer mediaPlayer;
    private Timer timer;//定时器
    private String path = "/storage/emulated/0/aatest/input.mp3";
    private boolean isSeekbarChaning;//互斥变量,防止进度条和定时器冲突
    private OnMyPreparedListener mOnMyPreparedListener;

    public MyMusicPlayer(){
        initMediaPlayer();
    }

    private void initMediaPlayer() {
        mediaPlayer = new MediaPlayer();
        try {
            //设置播放音频文件路径
            mediaPlayer.setDataSource(path);
            //设置播放流媒体类型。
            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            //设置循环播放
            mediaPlayer.setLooping(false);
            //同步的方式装载流媒体文件
            //mediaPlayer.prepare();
            // 通过异步的方式装载媒体资源
            mediaPlayer.prepareAsync();
            //当装载流媒体完毕的时候回调。
            mediaPlayer.setOnPreparedListener(this);
            //当流媒体播放完毕的时候回调。
            mediaPlayer.setOnCompletionListener(this);
            //当播放中发生错误的时候回调。
            mediaPlayer.setOnErrorListener(this);
            //当使用seekTo()设置播放位置的时候回调
            mediaPlayer.setOnSeekCompleteListener(this);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     *
     * @param time
     * @return
     */
    //public
    //传入的数据为毫秒数
    public  String formattime(long time){
        String min=  (time/(1000*60))+"";
        String second= (time%(1000*60)/1000)+"";
        if(min.length()<2){
            min=0+min;
        }
        if(second.length()<2){
            second=0+second;
        }
        return min+":"+second;
    }
    //计算播放时间
    public String calculateTime(int time){
        int minute;
        int second;
        if(time > 60){
            minute = time / 60;
            second = time % 60;
            //分钟再0~9
            if(minute >= 0 && minute < 10){
                //判断秒
                if(second >= 0 && second < 10){
                    return "0"+minute+":"+"0"+second;
                }else {
                    return "0"+minute+":"+second;
                }
            }else {
                //分钟大于10再判断秒
                if(second >= 0 && second < 10){
                    return minute+":"+"0"+second;
                }else {
                    return minute+":"+second;
                }
            }
        }else if(time < 60){
            second = time;
            if(second >= 0 && second < 10){
                return "00:"+"0"+second;
            }else {
                return "00:"+ second;
            }
        }
        return null;
    }
    //当装载流媒体完毕的时候回调。
    @Override
    public void onPrepared(MediaPlayer mp) {
        Log.e(TAG, "onPrepared()");
    }
    //当流媒体播放完毕的时候回调。
    @Override
    public void onCompletion(MediaPlayer mp) {
        Log.e(TAG, "onCompletion()");
        mOnMyPreparedListener.onMyPrepared();
    }
    //当播放中发生错误的时候回调。
    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        Log.e(TAG, "onError()");
        return false;
    }
    //当使用seekTo()设置播放位置的时候回调
    @Override
    public void onSeekComplete(MediaPlayer mp) {
        mediaPlayer.seekTo(0);//在当前位置播放
    }

    /**
     * 播放
     * @param seekbar
     */
    public void Play(final SeekBar seekbar){
        if(mediaPlayer != null){
            mediaPlayer.start();
            int duration = mediaPlayer.getDuration();//获取音乐总时间
            seekbar.setMax(duration);//将音乐总时间设置为Seekbar的最大值
            timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    if(!isSeekbarChaning){
                        seekbar.setProgress(mediaPlayer.getCurrentPosition());
                    }
                }
            },0,50);

        }
    }

    /**
     * 暂停播放
     */
    public void Pause(){
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.pause();
        }
    }

    /**
     * 重新播放
     */
    public void Replay(){
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.seekTo(0);
        }
    }

    /**
     * 停止播放
     */
    public void Stop(){
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }

    /**
     *循环播放
     * @param looping
     */
    public void setLooping(boolean looping){
        mediaPlayer.setLooping(looping);
    }
    /**
     * 获取音乐总时长
     * @return
     */
    public int getDuration(){
        int duration = mediaPlayer.getDuration();
        return duration;
    }

    /**
     * 获取当前播放的位置
     * @return
     */
    public int getCurrentPosition(){
        int currentPosition = mediaPlayer.getCurrentPosition();
        return currentPosition;
    }

    /**
     * 设置当前MediaPlayer的播放位置,单位是毫秒
     * @param progress
     */
    public void setSeekto(int progress){
        mediaPlayer.seekTo(progress);//在当前位置播放
    }
    /**
     * 互斥变量,防止进度条和定时器冲突
     * @param isSeekbar
     */
    public void setSeekbarChaning(boolean isSeekbar){
        isSeekbarChaning = isSeekbar;
    }

    /**
     * 获取Seekbar状态
     * @return
     */
    public boolean isSeekbarChaning(){
        return isSeekbarChaning;
    }
    /**
     * 释放资源
     */
    public void release(){
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.stop();
            //回收流媒体资源
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }
    public void setOnMyPreparedListener(OnMyPreparedListener listener)
    {
        mOnMyPreparedListener = listener;
    }
    public interface OnMyPreparedListener
    {
      
        void onMyPrepared();
    }
}

2、布局文件如下所示

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <Button
        android:layout_width="match_parent"
        android:onClick="onPlay"
        android:text="播放"
        android:layout_height="wrap_content"/>
    <Button
        android:layout_width="match_parent"
        android:onClick="onPause"
        android:text="暂停"
        android:layout_height="wrap_content"/>
    <Button
        android:layout_width="match_parent"
        android:onClick="onReplay"
        android:text="重新播放"
        android:layout_height="wrap_content"/>
    <Button
        android:layout_width="match_parent"
        android:onClick="onLooping"
        android:text="循环播放"
        android:layout_height="wrap_content"/>
    <Button
        android:layout_width="match_parent"
        android:onClick="onStop"
        android:text="停止播放"
        android:layout_height="wrap_content"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_marginTop="20dp"
        android:layout_height="wrap_content">

        <TextView
            android:layout_marginLeft="10dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/tv_start" />

        <SeekBar
            android:layout_width="250dp"
            android:layout_height="wrap_content"
            android:id="@+id/seekbar" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/tv_end" />
    </LinearLayout>
    <TextView
        android:id="@+id/sample_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        android:text="Hello World!"
         />

</LinearLayout>

3、在AndroidManifest.xml中添加权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
android:requestLegacyExternalStorage="true"
public class PermissionsManagement {
    private static final String TAG = "PermissionsManagement";
    public static void requestMyPermissions(Activity mActivity) {

        if (ContextCompat.checkSelfPermission(mActivity,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            //没有授权,编写申请权限代码
            ActivityCompat.requestPermissions(mActivity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
        } else {
            Log.d(TAG, "requestMyPermissions: 有写SD权限");
        }
        if (ContextCompat.checkSelfPermission(mActivity,
                Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            //没有授权,编写申请权限代码
            ActivityCompat.requestPermissions(mActivity, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 100);
        } else {
            Log.d(TAG, "requestMyPermissions: 有读SD权限");
        }
    }
}

4、然后在MainActivity 实现功能。

public class MainActivity extends AppCompatActivity implements MyMusicPlayer.OnMyPreparedListener {
    private static final String TAG = "wq892373445";
    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }
    private MyMusicPlayer mMyMusicPlayer;
    //显示流媒体的总播放时长
    private TextView tv_end;
    private SeekBar seekbar;
    private TextView tv_start;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        PermissionsManagement.requestMyPermissions(this);
        seekbar = (SeekBar)findViewById(R.id.seekbar);
        tv_end = (TextView)findViewById(R.id.tv_end);
        tv_start = (TextView)findViewById(R.id.tv_start);

        //绑定监听器,监听拖动到指定位置
        seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {


            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                int duration2 = mMyMusicPlayer.getDuration() / 1000;//获取音乐总时长
                int position = mMyMusicPlayer.getCurrentPosition();//获取当前播放的位置
                tv_start.setText(mMyMusicPlayer.calculateTime(position / 1000));//开始时间
                tv_end.setText(mMyMusicPlayer.calculateTime(duration2));//总时长

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                mMyMusicPlayer.setSeekbarChaning(true);
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                mMyMusicPlayer.setSeekbarChaning(false);
                mMyMusicPlayer.setSeekto(seekBar.getProgress());//在当前位置播放
                tv_start.setText(mMyMusicPlayer.formattime(mMyMusicPlayer.getCurrentPosition()));
            }
        });
        mMyMusicPlayer = new MyMusicPlayer();
        mMyMusicPlayer.setOnMyPreparedListener(this);
        int duration2 = mMyMusicPlayer.getDuration() / 1000;//获取音乐总时长
        int position = mMyMusicPlayer.getCurrentPosition();//获取当前播放的位置
        tv_start.setText(mMyMusicPlayer.calculateTime(position / 1000));//开始时间
        tv_end.setText(mMyMusicPlayer.calculateTime(duration2));//总时长
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();


    @Override
    protected void onDestroy() {
        mMyMusicPlayer.release();
        super.onDestroy();
    }

    /**
     * 播放
     * @param view
     */
    public void onPlay(View view) {
        mMyMusicPlayer.Play(seekbar);
    }

    /**
     * 暂停播放
     * @param view
     */
    public void onPause(View view) {
        mMyMusicPlayer.Pause();
    }

    /**
     * 重新播放
     * @param view
     */
    public void onReplay(View view) {
        mMyMusicPlayer.Replay();
    }

    /**
     * 停止播放
     * @param view
     */
    public void onStop(View view) {
        mMyMusicPlayer.Stop();
    }

    /**
     * 循环播放
     * @param view
     */
    public void onLooping(View view) {
        mMyMusicPlayer.setLooping(true);
    }
    @Override
    public void onMyPrepared() {
        Log.d(TAG, "onPrepared()");
        // 装载完毕回调
        //获取流媒体的总播放时长,单位是毫秒。
        tv_end.setText(mMyMusicPlayer.calculateTime((mMyMusicPlayer.getDuration()/ 1000)));
        Log.d(TAG, "总的播放时长:"+mMyMusicPlayer.getDuration());
        //获取当前流媒体的播放的位置,单位是毫秒
        tv_start.setText(mMyMusicPlayer.calculateTime((mMyMusicPlayer.getCurrentPosition()/ 1000)));
    }


}
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值