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~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的使用以及实现音频播放器相关推荐
- Android 中封装优雅的 MediaPlayer 音频播放器,支持多个播放器
Android 中封装优雅的 MediaPlayer 音频播放器,支持多个播放器实例的示例: public class AudioPlayer implements MediaPlayer.OnPre ...
- Android media ---- 1.7.ffmpeg 简单音频播放器
哎,喜欢偷懒,这边直接抄袭下雷神的代码.雷神是个值得敬佩的程序员. vs代码下载链接: https://pan.baidu.com/s/1c2dIuYk 密码:ld4b /* *最简单的基于FFmpe ...
- Android 使用 MediaCodec API音频播放器
OpenMAX 有时缩写为"OMX",是针对多媒体处理的免版税.跨平台的例程和接口集.它主要用于嵌入式设备,在设计时考虑了效率,因此可以以优化和受控的方式处理大量多媒体数据,包括视 ...
- 【创新项目实训】Android移动开发教学app案例之音频播放器(二)
教学app案例之音频播放器(二) 一.开发环境 二.项目介绍 三.开始主界面 四.显示歌曲列表 五.播放音乐的服务 六.实现播放音乐 一.开发环境 软件环境: Android Studio 4.1.3 ...
- android 以音频播放器为例实现通知栏显示通知,并实现切歌、暂停、播放,并实现加载网络图片,并实现关闭第三方APP音频
首先先给大家看下效果 接下来我们看下具体如何实施 1.首先我们创建一个音频的单例对象,这样能保证每次在播放的的音频是唯一的(类名如:MediaPlayerUtil.java) package xxx; ...
- 视频教程-FFmpeg打造Android万能音频播放器-Android
FFmpeg打造Android万能音频播放器 从事Android移动端开发多年.主导开发过直播.电商.聊天等各种类型APP和游戏SDK:熟悉Android音视频开发.底层NDK开发等:有开源项目:ht ...
- 原创:Android应用开发记录-Andorid歌词秀(3)使用的Service的音频播放器
在下文中我们实现的简单的音乐播放功能. 原创:Android应用开发记录-Andorid歌词秀(2)先来一个音频播放器 但是这样的方式在程序退出后播放也会停止,为了解决这个问题,使用的Service类 ...
- Android录音笔-音频播放器,不止适用于保存的录音
Android录音笔-音频播放器,不止适用于保存的录音 功能说明 点击录音列表项弹出播放对话框 对话框碎片的布局dialog_fragment.xml 对话框碎片MyDialog.java 对话框碎片 ...
- Android Studio——简易音频播放器
目的 设计一个具有选歌功能的音频播放器 工具及环境 使用java语言,在Android studio平台上进行开发 功能设计 界面有三个按钮选项,可以停止.播放.暂停音乐.通过选择列表的音乐,播放相应 ...
最新文章
- 比Momentum更快:揭开Nesterov Accelerated Gradient的真面目NAG 梯度下降
- BidNet:无视差估计的双目图像去雾(CVPR2020)
- su oracle : 只切换用户,而不切换环境;就是说切到oracle用户后,使用的依旧是root的环境。 su - oracle :同时切换用户和环境
- 【Android开发—智能家居系列】(一):智能家居原理
- win7纯净版下载csdn_win10原版纯净版下载,安装技巧
- 对一道基础string题及其变式题的思考与解析
- 什么是数据脱敏(Data Masking)?
- 1000以内完数c语言程序_C语言完数编程题目详解
- oj1029统计素数并求和
- Android对话框控件读写,Android 对话框控件
- CSS实现选中图片效果
- 微信小程序-仿微信朋友圈
- 制作多关卡系统 func_brush
- 【Android开发-4】进入实践,最喜欢折腾的计算器
- ad19怎么手动布线_AD18/19自动布线之坑
- 人到中年怎样防止头发花白
- Linux---笔记总结
- CRUP为后面使用框架在页面上增删改查做下铺垫.
- Found my pics from 2007
- 面试中的65个技巧性回答---这简直就是全餐嘛