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

前言

最近项目开发中需要用到 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 用法参照官网教程 :


问题二 ~ 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 这个异常也算了解了点, 而目前也暂只能以这种说法为指导进行开发, 后续有时间再深入源码一探究竟.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值