一、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~9if(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;}//当装载流媒体完毕的时候回调。@Overridepublic void onPrepared(MediaPlayer mp) {Log.e(TAG, "onPrepared()");}//当流媒体播放完毕的时候回调。@Overridepublic void onCompletion(MediaPlayer mp) {Log.e(TAG, "onCompletion()");mOnMyPreparedListener.onMyPrepared();}//当播放中发生错误的时候回调。@Overridepublic boolean onError(MediaPlayer mp, int what, int extra) {Log.e(TAG, "onError()");return false;}//当使用seekTo()设置播放位置的时候回调@Overridepublic 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() {@Overridepublic 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"><Buttonandroid:layout_width="match_parent"android:onClick="onPlay"android:text="播放"android:layout_height="wrap_content"/><Buttonandroid:layout_width="match_parent"android:onClick="onPause"android:text="暂停"android:layout_height="wrap_content"/><Buttonandroid:layout_width="match_parent"android:onClick="onReplay"android:text="重新播放"android:layout_height="wrap_content"/><Buttonandroid:layout_width="match_parent"android:onClick="onLooping"android:text="循环播放"android:layout_height="wrap_content"/><Buttonandroid:layout_width="match_parent"android:onClick="onStop"android:text="停止播放"android:layout_height="wrap_content"/><LinearLayoutandroid:layout_width="match_parent"android:layout_marginTop="20dp"android:layout_height="wrap_content"><TextViewandroid:layout_marginLeft="10dp"android:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/tv_start" /><SeekBarandroid:layout_width="250dp"android:layout_height="wrap_content"android:id="@+id/seekbar" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/tv_end" /></LinearLayout><TextViewandroid: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;@Overrideprotected 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() {@Overridepublic 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));//总时长}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {mMyMusicPlayer.setSeekbarChaning(true);}@Overridepublic 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();@Overrideprotected 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);}@Overridepublic 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)));}}

Android MediaPlay的使用以及实现音频播放器相关推荐

  1. Android 中封装优雅的 MediaPlayer 音频播放器,支持多个播放器

    Android 中封装优雅的 MediaPlayer 音频播放器,支持多个播放器实例的示例: public class AudioPlayer implements MediaPlayer.OnPre ...

  2. Android media ---- 1.7.ffmpeg 简单音频播放器

    哎,喜欢偷懒,这边直接抄袭下雷神的代码.雷神是个值得敬佩的程序员. vs代码下载链接: https://pan.baidu.com/s/1c2dIuYk 密码:ld4b /* *最简单的基于FFmpe ...

  3. Android 使用 MediaCodec API音频播放器

    OpenMAX 有时缩写为"OMX",是针对多媒体处理的免版税.跨平台的例程和接口集.它主要用于嵌入式设备,在设计时考虑了效率,因此可以以优化和受控的方式处理大量多媒体数据,包括视 ...

  4. 【创新项目实训】Android移动开发教学app案例之音频播放器(二)

    教学app案例之音频播放器(二) 一.开发环境 二.项目介绍 三.开始主界面 四.显示歌曲列表 五.播放音乐的服务 六.实现播放音乐 一.开发环境 软件环境: Android Studio 4.1.3 ...

  5. android 以音频播放器为例实现通知栏显示通知,并实现切歌、暂停、播放,并实现加载网络图片,并实现关闭第三方APP音频

    首先先给大家看下效果 接下来我们看下具体如何实施 1.首先我们创建一个音频的单例对象,这样能保证每次在播放的的音频是唯一的(类名如:MediaPlayerUtil.java) package xxx; ...

  6. 视频教程-FFmpeg打造Android万能音频播放器-Android

    FFmpeg打造Android万能音频播放器 从事Android移动端开发多年.主导开发过直播.电商.聊天等各种类型APP和游戏SDK:熟悉Android音视频开发.底层NDK开发等:有开源项目:ht ...

  7. 原创:Android应用开发记录-Andorid歌词秀(3)使用的Service的音频播放器

    在下文中我们实现的简单的音乐播放功能. 原创:Android应用开发记录-Andorid歌词秀(2)先来一个音频播放器 但是这样的方式在程序退出后播放也会停止,为了解决这个问题,使用的Service类 ...

  8. Android录音笔-音频播放器,不止适用于保存的录音

    Android录音笔-音频播放器,不止适用于保存的录音 功能说明 点击录音列表项弹出播放对话框 对话框碎片的布局dialog_fragment.xml 对话框碎片MyDialog.java 对话框碎片 ...

  9. Android Studio——简易音频播放器

    目的 设计一个具有选歌功能的音频播放器 工具及环境 使用java语言,在Android studio平台上进行开发 功能设计 界面有三个按钮选项,可以停止.播放.暂停音乐.通过选择列表的音乐,播放相应 ...

最新文章

  1. 比Momentum更快:揭开Nesterov Accelerated Gradient的真面目NAG 梯度下降
  2. BidNet:无视差估计的双目图像去雾(CVPR2020)
  3. su oracle : 只切换用户,而不切换环境;就是说切到oracle用户后,使用的依旧是root的环境。 su - oracle :同时切换用户和环境
  4. 【Android开发—智能家居系列】(一):智能家居原理
  5. win7纯净版下载csdn_win10原版纯净版下载,安装技巧
  6. 对一道基础string题及其变式题的思考与解析
  7. 什么是数据脱敏(Data Masking)?
  8. 1000以内完数c语言程序_C语言完数编程题目详解
  9. oj1029统计素数并求和
  10. Android对话框控件读写,Android 对话框控件
  11. CSS实现选中图片效果
  12. 微信小程序-仿微信朋友圈
  13. 制作多关卡系统 func_brush
  14. 【Android开发-4】进入实践,最喜欢折腾的计算器
  15. ad19怎么手动布线_AD18/19自动布线之坑
  16. 人到中年怎样防止头发花白
  17. Linux---笔记总结
  18. CRUP为后面使用框架在页面上增删改查做下铺垫.
  19. Found my pics from 2007
  20. 面试中的65个技巧性回答---这简直就是全餐嘛

热门文章

  1. 将数字金额转换为中文大写金额(在网上找的)
  2. ps去掉图片白色背景的过程
  3. 职场白领强迫症三大典型症状
  4. 再谈意图识别与语义槽填充
  5. 计算机联网显示651,651错误代码,教您宽带连接提示错误651怎么办
  6. 【SVM分类】基于灰狼算法优化SVM实现数据分类matlab源码
  7. Venμs高分辨率(5米)多波段(12个)遥感数据介绍及下载
  8. 关于在苹果浏览器中new Date()函数兼容性问题
  9. 转载自:【腾讯优测干货分享】Android5.0-6.0双卡适配指南
  10. ider编辑器汉化补丁包安装