目录

一、官方资料

二、简单介绍

三、MediaPlayer使用

1.创建MediaPlayer实例

2.重要API

3.状态图

4.代码

5.常用API

6.辅助效果

总结


一、官方资料

MediaPlayer 概览https://developer.android.google.cn/guide/topics/media/mediaplayer?hl=zh_cn

MediaPlayer 文档https://developer.android.google.cn/reference/kotlin/android/media/MediaPlayer

其实,通过阅读上面两个文档,基本上就可以去实现自己的需求了,毕竟每个人的需求不一样。由于需求只用到播放音频部分,所以不会设计涉及视频播放。

二、简单介绍

官方概述是这样介绍的:

Android 多媒体框架支持播放各种常见媒体类型,以便您轻松地将音频、视频和图片集成到应用中。您可以使用 MediaPlayer API,播放存储在应用资源(原始资源)内的媒体文件、文件系统中的独立文件或者通过网络连接获得的数据流中的音频或视频。

总之,MediaPlayer既可以播放音视频。MediaPlayer位于android.media包下,media下一些常用的API:

  • MediaPlayer :可用于控制音频/视频文件和流的播放。 可以在VideoView找到有关如何使用此类中的方法的示例。
  • AudioTrack :为应用程序管理和播放单个音频资源。 它允许将 PCM 音频缓冲区流式传输到音频接收器以进行播放。
  • MediaCodec :可用于访问低级媒体编解码器,即编码器/解码器组件。 它是 Android 底层多媒体支持基础设施的一部分(通常与MediaExtractor 、 MediaSync 、 MediaMuxer 、 MediaCrypto 、 MediaDrm 、 Image 、 Surface和AudioTrack 。)
  • MediaRecorder:用于录制音频和视频。
  • VolumeShaper:类用于在媒体播放期间自动控制音频音量,允许简单实现过渡效果和闪避。

言归正传,继续介绍MediaPlayer。使用前注意:MediaPlayer 不是线程安全的。实例的创建和所有访问都应该在同一个线程上。

如果使用 MediaPlayer 流式播放基于网络的内容,则应用必须申请网络访问权限:

 <uses-permission android:name="android.permission.INTERNET" />

如果播放器应用需要防止屏幕变暗或处理器进入休眠状态,则需要权限:

<uses-permission android:name="android.permission.WAKE_LOCK" />

三、MediaPlayer使用

1.创建MediaPlayer实例

创建MediaPlayer,有两种方式,一种是先new出一个实例,然后调用setDataSource()方法。

MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(getApplicationContext(), myUri);

另一种是直接调用静态方法create()创建实例。

//保存在应用的 res/raw/ 目录中的音频资源:R.raw.sound_file_1
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);

这两种方式都有多个方法重载,根据不同的音频资源选择创建方式:

public void setDataSource (String path)
public void setDataSource (FileDescriptor fd)
public void setDataSource (Context context, Uri uri)
public void setDataSource (FileDescriptor fd, long offset, long length)public static MediaPlayer create(Context context, Uri uri)
public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder)
public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder,AudioAttributes audioAttributes, int audioSessionId)
public static MediaPlayer create(Context context, int resid)
public static MediaPlayer create(Context context, int resid,AudioAttributes audioAttributes, int audioSessionId)

列举常用的创建并设置资源的方式:

//1.本地原始资源(保存在应用的 res/raw/ 目录中),该资源的内容不应为原始音频(pcm)。它应该是采用某种支持的格式且经过适当编码和格式化的媒体文件(比如:MP3)
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);//2.如果你已经获取到Uri
Uri myUri = ....; // initialize Uri here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);//3.通过 HTTP 流式传输并播放远程网址上的内容
String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);//4.如果已知本地音频文件路径:
String path = "...";
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(path);//5.如果资源放在assest下
String path = "..."
AssetFileDescriptor fd = getAssets().openFd(path);
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());

2.重要API

创建并设置好资源后,调用prepare()或者prepareAsync()来接在资源,接着调用start()开始播放。注意: 如果是使用create()方法创建mediaplayer实例的,不需要调用prepare()方法,直接调用start()即可。因为create()会自动调用prepare()。

//1.同步
mediaPlayer.prepare();//同步,会阻塞
mediaPlayer.start();//开始播放//2.异步
mediaPlayer.prepareAsync();
player.setOnPreparedListener(mediaPlayer -> {mediaPlayer.start();
});

除了onStart()开始播放,还有onPause()暂停播放,onStop()停止播放,seekTo()指定某个时间点开始播放,onRelease()释放资源,reset()重置等

3.状态图

那么这些API,按照什么逻辑来使用呢,这里要重点看下官方给出的状态图(建议来来回回多看几遍):

没什么好说的,文档已经说的很清楚了:

您还应该记住,MediaPlayer 以状态为基础。也就是说,MediaPlayer 具有内部状态,您在编写代码时必须始终注意,某些操作仅在播放器处于特定状态时才有效。如果您在错误的状态下执行某项操作,则系统可能会抛出异常或导致其他不良行为。

MediaPlayer 类的参考文档显示了一个完整的状态图,该图说明了哪些方法可将 从一种状态变为另一种状态。例如,当您创建新的 MediaPlayer 时,它处于“Idle”状态。此时,您应该通过调用 setDataSource() 初始化该类,使其处于“Initialized”状态。然后,您必须使用 prepare() 或 prepareAsync() 方法完成准备工作。当 MediaPlayer 准备就绪后,它便会进入“Prepared”状态,这也意味着您可以通过调用 start() 使其播放媒体内容。此时,如图所示,您可以通过调用 start()pause() 和 seekTo() 等方法在“Started”、“Paused”和“PlaybackCompleted”状态之间切换。不过请注意,当您调用 stop() 时,除非您再次准备 MediaPlayer,否则将无法再次调用 start()

在编写与 MediaPlayer 对象互动的代码时,请始终牢记该状态图,因为从错误的状态调用其方法是导致错误的常见原因。

4.代码

上边的状态图基本指导了我们应用程序如何编写,用一个枚举类,把所有状态列出来:

    enum MediaPlayStatus {IDLE,INITIALIZED,PREPARING,PREPARED,STARTED,PAUSED,STOPPED,ERROR,          END            }

要严格根据状态图来编写代码,不然很容易出错,下面是自己写的一个MediaPlayer的封装

public class MediaPlayerUtil {private static final String TAG = MediaPlayerUtil.class.getSimpleName();public MediaPlayerUtil() {}private static final int HANDLER_PLAY_TIME = 1;//进度条状态刷新时间间隔private static final int TIMER_SEEK = 20;//播放器private MediaPlayer player;private Handler mUiHandler; //刷新uiprivate Handler mTimerHandler;//定时轮询:更新seekBarprivate Runnable timerRun;//定时任务private MediaPlayStatus status = MediaPlayStatus.IDLE;private ExecutorService singleExecutor;public void init() {singleExecutor = Executors.newSingleThreadExecutor();initData();initPlay();}private void initData() {mUiHandler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);//更新当前时间try {int curP = player.getCurrentPosition();if (mListener != null) {mListener.onSeekUpdate(curP);}} catch (Exception e) {Log.i(TAG, "handleMessage: e = " + e);reset();}}};HandlerThread handlerThread = new HandlerThread("media_timer");handlerThread.start();mTimerHandler = new Handler(handlerThread.getLooper());timerRun = new Runnable() {@Overridepublic void run() {mTimerHandler.postDelayed(this, TIMER_SEEK);Message msg = Message.obtain();msg.what = HANDLER_PLAY_TIME;mUiHandler.sendMessage(msg);}};}private void initPlay() {Log.i(TAG, "initPlay: status = " + status);player = new MediaPlayer();status = MediaPlayStatus.IDLE;//当流媒体播放完毕的时候回调。player.setOnCompletionListener(mediaPlayer -> {Log.i(TAG, "OnCompletion: status = " + status);stopTimer();status = MediaPlayStatus.PLAYBACK_COMPLETED;if (mListener != null) {mListener.onCompletion();}});//需要注意的是,一旦发生错误,即使应用程序尚未注册错误侦听器,MediaPlayer 对象也会进入Error状态。//为了从处于 Error状态的 MediaPlayer 对象并从错误中恢复,reset()可以调用将对象恢复到其Idle 状态。player.setOnErrorListener((mediaPlayer, i, i1) -> {status = MediaPlayStatus.ERROR;if (mListener != null) {return mListener.onError();//在外部onError回调中调用reset()重置状态,并重置相关UI状态}//如果该方法处理了错误,则为true,否则为false。 返回false或根本没有OnErrorListener,// 将导致调用OnCompletionListenerreturn true;});//prepare完成后,才可以播放->startplayer.setOnPreparedListener(mediaPlayer -> {Log.i(TAG, "Prepared: status = " + status);status = MediaPlayStatus.PREPARED;//取消转圈if (mListener != null) {mListener.onPrepared();}//可以准备好自动start(),也可以自己控制start()。//start();});}//资源可能为空,或者太大,获取其他原因导致无法加载,所以加上try catchpublic void prepare(String audioPath) throws IOException {//先保证恢复空闲状态if (player != null) {reset();}//设置资源if (status == MediaPlayStatus.IDLE) {player.setDataSource(audioPath);status = MediaPlayStatus.INITIALIZED;}//准备if (status == MediaPlayStatus.INITIALIZED || status == MediaPlayStatus.STOPPED) {player.prepareAsync();status = MediaPlayStatus.PREPARING;}}//暂停播放。 调用 start() 恢复。public void pause() {Log.i(TAG, "pause: status = " + status);if (status == MediaPlayStatus.STARTED || status == MediaPlayStatus.PAUSED) {singleExecutor.execute(() -> {player.pause();status = MediaPlayStatus.PAUSED;});//停止刷新进度条stopTimer();}}//播放开始或暂停后停止播放。//调用 stop()停止播放并导致处于Started、Paused、Prepared 或PlaybackCompleted状态的 MediaPlayer进入 Stopped状态。//一旦处于Stopped状态,播放不能开始,直到prepare()或被prepareAsync()调用以将 MediaPlayer 对象再次设置为Prepared状态。public void stop() {Log.i(TAG, "stop: status = " + status);if (status == MediaPlayStatus.PAUSED || status == MediaPlayStatus.STARTED || status == MediaPlayStatus.PREPARED|| status == MediaPlayStatus.PLAYBACK_COMPLETED || status == MediaPlayStatus.STOPPED) {singleExecutor.execute(() -> {player.stop();status = MediaPlayStatus.STOPPED;});stopTimer();}}//开始或恢复播放。 如果之前已暂停播放,将从暂停的位置继续播放。 如果播放已停止或从未开始,播放将从头开始。//isPlaying()可以调用来测试MediaPlayer对象是否处于Started状态public void start() {Log.i(TAG, "start: status = " + status);if (status == MediaPlayStatus.PREPARED || status == MediaPlayStatus.STARTED|| status == MediaPlayStatus.PAUSED || status == MediaPlayStatus.PLAYBACK_COMPLETED) {singleExecutor.execute(() -> {player.start();status = MediaPlayStatus.STARTED;});startTimer();}}public void seekTo(int position) {Log.i(TAG, "seekTo: status = " + status);if (status == MediaPlayStatus.PREPARED || status == MediaPlayStatus.STARTED|| status == MediaPlayStatus.PAUSED || status == MediaPlayStatus.PLAYBACK_COMPLETED) {singleExecutor.execute(() -> {player.seekTo(position);});}}//将 MediaPlayer 重置为其未初始化状态。 调用此方法后,您必须通过设置数据源并调用prepare() 来再次初始化它public void reset() {Log.i(TAG, "reset: status = " + status);player.reset();status = MediaPlayStatus.IDLE;}//開始計時public void startTimer() {Log.i(TAG, "startTimer: ");mTimerHandler.postDelayed(timerRun, TIMER_SEEK);}//停止计时public void stopTimer() {Log.i(TAG, "stopTimer: ");mTimerHandler.removeCallbacks(timerRun);}//释放与此 MediaPlayer 对象关联的资源。 使用完 MediaPlayer 后调用此方法被认为是一种很好的做法public void release() {Log.i(TAG, "release: status = " + status);singleExecutor.execute(() -> {player.release();player = null;status = MediaPlayStatus.END;});stopTimer();}public void onDestroy() {release();if (mUiHandler != null) {mUiHandler.removeCallbacksAndMessages(null);}mListener = null;}//播放状态监听public interface OnMediaStateListener {//准备完成void onPrepared();//当前播放进度void onSeekUpdate(int curTimeInt);//播放完成void onCompletion();boolean onError();}private OnMediaStateListener mListener;public void setOnMediaStateListener(OnMediaStateListener listener) {mListener = listener;}//MediaPlay生命周期在END和IDLE状态中间流转enum MediaPlayStatus {//空闲状态//1.使用new创建->IDLE//2.调用reset()->IDLE//这两种方式的区别是:如果测试发生错误(调用getCurrentPosition()等方法),//1方式不会回调方法 OnErrorListener.onError() 并且对象状态保持不变//2方式会回调方法 OnErrorListener.onError()并且对象将转移到ERROR状态IDLE,//初始化状态//1.setDataSource()->INITIALIZEDINITIALIZED,//1.INITIALIZED->prepareAsync()->PREPARING//2.STOPPED->prepareAsync()->PREPARINGPREPARING,//MediaPlayer 对象必须先进入Prepared状态,然后才能开始播放//1.PREPARING->OnPreparedListener.onPrepare()->PREPARED//2.INITIALIZED->prepare()->PREPARED//3.STOPPED->prepare()->PREPARED//4.PREPARED->seekTo()->PREPAREDPREPARED,//1.PREPARED->start()->STARTED//2.PAUSED->start()->STARTED//3.STARTED->seekTo(),start()->STARTED//4.PLAYBACK_COMPLETED&&setLooping(true)->STARTED//5.PLAYBACK_COMPLETED->start()->STARTEDSTARTED,//1.STARTED->pause()->PAUSED//2.PAUSED->pause(),seekTo()->PAUSEDPAUSED,//1.STARTED->stop()->STOPPED//2.PAUSED->stop()->PAUSE//3.PLAYBACK_COMPLETED->stop()->PAUSE//4.STOPPED->stop()->STOPPED//5.PREPARED->stop()->STOPPEDSTOPPED,//1.STARTED->PLAYBACK_COMPLETED&&setLooping(true)->PLAYBACK_COMPLETED//1.PLAYBACK_COMPLETED->seekTo()->PLAYBACK_COMPLETEDPLAYBACK_COMPLETED, //播放完成,在此状态调用seekTo()//1.onErrorListener.onError()ERROR,          //onErrorListener.onError -> ERROR//release()->END//一旦不再使用 MediaPlayer 对象,release()立即调用//一旦 MediaPlayer 对象处于End状态,就不能再使用它,也无法将其恢复到任何其他状态。END            //}}

使用:

MediaPlayerUtil mediaPlayerUtil = new MediaPlayerUtil();
mediaPlayerUtil.init();
mediaPlayerUtil.setOnMediaStateListener(new MediaPlayerUtil.OnMediaStateListener() {@Overridepublic void onPrepared() {//音频加载完成,有加载进度条的可以在这取消mediaPlayerUtil.start();}@Overridepublic void onSeekUpdate(int curTimeInt) {//更新进度条}@Overridepublic void onCompletion() {//播放完成}@Overridepublic boolean onError() {mediaPlayerUtil.reset();//重置UI状态return true;}
});try {mediaPlayerUtil.prepare("你的音频路径");
} catch (IOException e) {e.printStackTrace();
}

5.常用API

MediaPlayer还有一些比较常规的方法,我没用到,这里也列一下:

//如果当前正在播放,则为 true,否则为 false
public native boolean isPlaying();//设置此播放器的音量。请注意,传递的体积值是 0.0 到 1.0 范围内的原始标量。
public void setVolume(float leftVolume, float rightVolume);//将播放器设置为循环播放或非循环播放
public native void setLooping(boolean looping);//检查 MediaPlayer 是循环播放还是非循环播放
public native boolean isLooping();

如果需要调节因素或者音调:

//使用PlaybackParams设置播放速率。对象准备好后,零速调用就相当于调用pause()。
//对象准备好后,以非零速度调用它相当于调用start()
public native void setPlaybackParams(@NonNull PlaybackParams params);//使用
PlaybackParams playbackParams = new PlaybackParams();
playbackParams.setSpeed(6);//音速
playbackParams.setPitch(1);//音调
player.setPlaybackParams(playbackParams);

测试发现,播放音速最高只有6倍,再高就会崩溃。测试ExoPlayer最高8倍速。实际项目需求要20倍左右的。如果有能调高播放倍速的方法或者其他第三方的播放器(),还请不吝赐教。

6.辅助效果

MediaPlayer也可以配置一些辅助效果类一起使用,在android.media.audiofx包下(注意我是在Androidx环境下)。

这里边有很多辅助效果类,可以自己看下文档介绍,我只用到了Equalizer和Visualizer用来分析音频频率。频率分析这块内容比较多,之后会专门重点介绍。

如果需要两个音频无缝切换,则可以使用:

// 将 MediaPlayer 设置为在此 MediaPlayer 完成播放(即到达流的末尾)时启动。
// 媒体框架将尝试尽可能无缝地从这个播放器过渡到下一个播放器。
// 下一个玩家可以在完成之前的任何时间设置,但必须在成功调用 setDataSource 之后。
// 下一个播放器必须由应用程序准备,应用程序不应在其上调用 start()。
// 下一个 MediaPlayer 必须与“this”不同。 如果 next == this 会抛出异常。
// 应用程序可以调用 setNextMediaPlayer(null) 来指示不应在播放结束时启动下一个播放器。
// 如果当前播放器在循环播放,则继续循环播放,下一个播放器不会启动。
public native void setNextMediaPlayer(MediaPlayer next);

这个方法看起来有点疑惑,还要在创建一个MediaPlayer实例。那如果有多个音频拼接呢?创建两个实例交替设置?感觉太麻烦了。况且我只想有一个MediaPlayer实例,考虑再三,最终选择了ExoPlayer,嗯,真香!

总结

总的来说,使用mediaplayer播放音频还是比较简单的。做一个简单的音频播放器还是绰绰由有余的。支持的常用的格式也是足够多:支持的媒体格式。也可以配合一些辅助类完成特殊音效或者音频分析。

Android 使用MediaPlayer播放音频详解相关推荐

  1. Android使用MediaPlayer播放音频

    Android使用MediaPlayer播放音频 一.目标 二.最终实现 三.接下来 四.Finally 开发<Android高仿iOS Messages声音播放波形效果>完成后,Tape ...

  2. android播放mp3方法,Android之MediaPlayer播放音频与视频

    本节带来的是Android多媒体中的--MediaPlayer,我们可以通过这个API来播放音频和视频 该类是Androd多媒体框架中的一个重要组件,通过该类,我们可以以最小的步骤来获取,解码 和播放 ...

  3. 【Android游戏开发之八】游戏中添加音频-详解MediaPlayer与SoundPoo!并讲解两者的区别和游戏中的用途!...

    为什么80%的码农都做不了架构师?>>>     李华明Himi 原创,转载务必在明显处注明: 转载自 [黑米GameDev街区] 原文链接:  http://www.himigam ...

  4. 【Android游戏开发之八】游戏中添加音频-详解MediaPlayer与SoundPool的利弊以及各个在游戏中的用途!...

    本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/android-game/312.html 游 ...

  5. Android MediaPlayer 播放音频

    本文链接: Android MediaPlayer 播放音频 主要介绍使用MediaPlayer播放音频的方式.关于MediaPlayer的基础知识,比如状态,可以参考Android MediaPla ...

  6. Android Studio App开发中使用录音机、MediaRecorder录制音频和MediaPlayer播放音频讲解及实战(附源码)

    运行有问题或需要源码请点赞关注收藏后评论区留言~~~ 一.使用录音机录制音频 手机有自带的系统相机,也有自带的系统录音机,只要在调用startActivityForResult之前指定该动作,就会自动 ...

  7. android播放mp3方法,Android MediaPlayer 播放音频的方式

    主要介绍使用MediaPlayer播放音频的方式.关于MediaPlayer的基础知识,比如状态,可以参考Android MediaPlayer基础简介. 为了方便表达,定义变量名为mediaPlay ...

  8. mediaplayer android mp3 url,Android MediaPlayer 播放音频

    主要介绍使用MediaPlayer播放音频的方式.关于MediaPlayer的基础知识,比如状态,可以参考Android MediaPlayer 基础简介 为了方便表达,定义变量名为mediaPlay ...

  9. 《Android多媒体应用开发实战详解:图像、音频、视频、2D和3D》——1.3节搭建Android应用开发环境...

    本节书摘来自异步社区<Android多媒体应用开发实战详解:图像.音频.视频.2D和3D>一书中的第1章,第1.3节搭建Android应用开发环境,作者 王石磊 , 吴峥,更多章节内容可以 ...

最新文章

  1. Python之父:Python 4.0可能不会来了
  2. 生产环境:Nginx高可用方案
  3. python列表--查找集合中重复元素的个数
  4. 树莓派3b python3.6.1 SSL模块调用不起来的坑
  5. Bootstrap学习遇到的role属性--- 无障碍网页应用属性
  6. Apache Mahout:适合所有人的可扩展机器学习框架
  7. 最小覆盖模型matlab_数学规划模型的matlab求解 非线性最小二乘lsqnonlin
  8. c# mysql数据库查询语句_C# mysql 查询
  9. 为什么spring中的controller跳转出错_你的业务代码中Spring声明式事务处理正确了吗?
  10. ticwatch能支持鸿蒙吗,[杰瑞]安卓手表的最强形态,应该... - @魔法师蛋小丁 的微博精选 - 微博国际站...
  11. SpringBoot使用@Asyn注解注意事项
  12. 《Linux菜鸟入门2》访问网络文件系统
  13. python竖着输出_Python中三种格式化输出的方式
  14. 46. 考虑使用函数对象而不是函数作为STL算法的参数
  15. 平台式惯性导航系统简介(持续更新ing)
  16. android中java中的开方
  17. Excel的DATEDIF函数及其用法实例——求日期之间的间隔
  18. 中山医06年考研初试复试全攻略!( 完整版)
  19. 小武与剑指offer的恩怨情仇
  20. CSAPP 存储器山数据的测量以及绘制,Cache lab part A:Cache simulator

热门文章

  1. 狗狗有角膜溃疡怎么办?
  2. 系统集成项目管理之项目成本管理(EV AC PV CV SV)
  3. 复数java实验_java实验 复数
  4. 【机房收费系统】 之 收费模式
  5. matlab拉普拉斯因式分解,拉氏变换与反变换
  6. 解决闭包问题时 setTimeout执行顺序提前的问题
  7. PXE+KickStart自动化安装Linux系统
  8. cache在计算机中代表什么,cache是什么意思,计算机中cache是什么意思
  9. vue中实现汉字转化拼音
  10. 深信服上网行为管理(AC)部署三两事