Android 编程技巧之 ----- MediaPlayer 问题简记

    xiaoxiao2021-03-25  105

    前言问题一 播放系统文件无声 知识延伸 推荐链接 问题二 IllegalStateException 异常结语

    前言

    最近项目开发中需要用到 MediaPlayer 来播放声音文件, 在此之前是采用 Ringtone 类来实现, 考虑到 Ringtone 性能跟代码可控性最终还是使用 MediaPlayer 重新实现这个功能, 其中遇到了两个问题, 这里做个简单记录.


    问题一 ~ 播放系统文件无声

    先看看 第一版 问题代码 :

    public class SoundHelper { private static MediaPlayer mediaPlayer; public static void playSound(final Context context, final String audioPath) { Uri uri = Uri .parse("file:///system/media/audio/" + audioPath); if (mediaPlayer == null) { mediaPlayer = new MediaPlayer(); } if (uri != null) { try { if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); mediaPlayer.reset(); } mediaPlayer.setDataSource(context, uri); mediaPlayer .setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { mp.reset(); mp.release(); mp = null; return false; } }); mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion( MediaPlayer mp) { mp.reset(); mp.release(); mp = null; } }); mediaPlayer.prepare(); mediaPlayer.start(); } catch (Exception e) { e.printStackTrace(); } } } }

    经过多次测试, 一直无法播放系统中的声音文件, 最后参照 Android Developer 官网上的多媒体话题才发现, 使用 MediaPlayer 的步骤少了一步, 即设置播放声音源的类型, 即 :

    MediaPlayer.setAudioStreamType() 方法


    加上后, 第二版 代码经测试可正常播放系统声音文件 :

    public class SoundHelper { ...... mediaPlayer.setDataSource(context, uri); mediaPlayer .setAudioStreamType(AudioManager.STREAM_SYSTEM); ...... }

    知识延伸, 推荐链接

    更多 MediaPlayer 用法参照官网教程 :

    Android Developer 官网中 关于 MediaPlayer 的使用话题 (科学上网)Android Developer 国内官网中 关于 MediaPlayer 的使用话题 (直接访问)

    问题二 ~ IllegalStateException 异常

    测试第二版代码过程中发现, 初次播放声音一切正常, 但第二次调用该方法时就会抛出 IllegalStateException 异常, 异常抛出点为 :

    mediaPlayer.isPlaying()

    最初以为是程序逻辑问题, 回过头检查多次, 发现并无不妥, 思前想后, 僵了大半天, 怀疑是 MediaPlayer 资源未释放完全导致, 尝试在每次调用该函数前加入判断语句, 如以下 第三版 代码 :

    public class SoundHelper { ...... Uri uri = Uri .parse("file:///system/media/audio/" + audioPath); if (mediaPlayer != null) { mediaPlayer.release(); mediaPlayer = null; } if (mediaPlayer == null) { mediaPlayer = new MediaPlayer(); } ...... }

    在每次调用该函数时先判断 MediaPlayer 所指对象是否不为空, 是的话就释放资源并赋为 null, 后面再重新 new 一个新的 MediaPlayer 对象.

    经测试, 发现以上代码解决了问题, 不再抛出 IllegalStateException 异常, 可正常多次调用播放系统文件声音, 仍不解, 明明设置了 MediaPlayer.setOnCompletionListener 并且在其回调方法 onCompletion() 中也显式释放了 MediaPlayer 的相关资源, 可为何在第二次调用该方法时会判断出 MediaPlayer 所指对象不为空呢 ?

    仔细观察, 方法最前面判断逻辑的 MediaPlayer 引用是 mediaPlayer, 而 onCompletion() 方法中传过来的引用参数是 mp, 难道这两个引用所指对象不是同一个对象 ? 因为前面第三版的代码佐证了这种猜想.

    官网搜寻一番无果, 到 Google 搜索, 大概有如下解释 : Android使用MediaPlayer开发时抛IllegalStateException, 作者 : lovelease

    大概意思是说, mp 调用释放资源, 赋为 null 等操作只是操作了 Native 层的 MediaPlayer 对象, 却并无释放上层 Java 的 MediaPlayer 占用的资源, 引用 mediaPlayer 所指对象也并无影响.

    基于这种说法, 尝试去除 第三版 代码添加内容, 修改为 第四版 代码如下 :

    public class SoundHelper { private static MediaPlayer mediaPlayer; public static void playSound(final Context context, final String audioPath) { Uri uri = Uri .parse("file:///system/media/audio/" + audioPath); if (mediaPlayer == null) { mediaPlayer = new MediaPlayer(); } if (uri != null) { try { if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); mediaPlayer.reset(); } mediaPlayer.setDataSource(context, uri); mediaPlayer .setAudioStreamType(AudioManager.STREAM_SYSTEM); mediaPlayer .setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { mediaPlayer.reset(); mediaPlayer.release(); mediaPlayer = null; return false; } }); mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion( MediaPlayer mp) { mediaPlayer.reset(); mediaPlayer.release(); mediaPlayer = null; } }); mediaPlayer.prepare(); mediaPlayer.start(); } catch (Exception e) { e.printStackTrace(); } } } }

    即不再使用回调函数传递过来的 mp 引用来释放资源, 直接使用自己声明的 mediaPlayer 引用来完成工作.

    经多次测试, 发现也不再出现 IllegalStateException 异常, 播放系统声音文件一切正常.


    结语

    经过一番折腾, 感觉 MediaPlayer 还是一个比较磨人的东西, 虽然不知此处理方式是否正确, 但对 IllegalStateException 这个异常也算了解了点, 而目前也暂只能以这种说法为指导进行开发, 后续有时间再深入源码一探究竟.

    转载请注明原文地址: https://ju.6miu.com/read-25257.html

    最新回复(0)