Android:使用MediaPlayer 播放音频及android.media.MediaPlayer._setDataSource(Native Method)异常解决方法

使用MediaPlayer播放本地音频时报错:

04-22 14:46:59.852  2996  3110 E AndroidRuntime: java.lang.IllegalStateException
04-22 14:46:59.852  2996  3110 E AndroidRuntime:        at android.media.MediaPlayer._setDataSource(Native Method)
04-22 14:46:59.852  2996  3110 E AndroidRuntime:        at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1154)
04-22 14:46:59.852  2996  3110 E AndroidRuntime:        at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1139)
04-22 14:46:59.852  2996  3110 E AndroidRuntime:        at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1118)
04-22 14:46:59.852  2996  3110 E AndroidRuntime:        at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1067)
04-22 14:46:59.852  2996  3110 E AndroidRuntime:        at com.demo.CoreController.startMediaPlayer(CoreController.java:1595)
...
04-22 14:46:59.852  2996  3110 E AndroidRuntime:        at java.lang.Thread.run(Thread.java:818)

原因分析:

    Android的MediaRecorder 和 MediaPlayer (MediaRecorder ,MediaPlayer 都是用c++实现的)API中用到了JNI。此异常的大部分原因是java里面的MediaRecorder /MediaPlayer 对象和native的对象不一致导致的。由于java会分别在堆内存和栈内存中操作(堆中放内容,栈中放地址,栈中的地址指向堆中内容)。在java中释放掉MediaRecorder /MediaPlayer的对象时,释放的是jni的对象,而java中只释放了栈中的内容,而堆中的内容没有释放。说到底就是没释放干净,导致出现对象不一致。

解决办法:

    在使用setDataSource()前使用reset()方法重置MediaPlayer为未初始化状态(即创建MediaPlayer 后加入的资源路径、流类型等)。

注意:使用reset()方法后必须设置数据源并调用prepare()(同步的方式装载流媒体文件方法)或prepareAsync()(异步的方式装载流媒体文件方法)再次对其进行初始化。

代码示例:

注意:在setDataSource()方法的异常里一定要加如下逻辑,目前遇到(低概率)使用reset()方法后底层native状态有时会与当前创建的MediaPlayer对象不一致也会造成IllegalStateException异常,若加上如下规避措施,即使遇到低概率异常也不会影响音频的播放。

                //重置MediaPlayer对象,使新的MediaPlayer对象与native状态一致
                mMediaPlayer.release();
                mMediaPlayer = null;
                mMediaPlayer = new MediaPlayer();
                mMediaPlayer.setDataSource(DIR_VOICEPATH + name);

具体如下:

    /**
     * 通过MediaPlayer开始播放音频
     * @param name
     */
    public void startMediaPlayer(String name) {
        Log.i(TAG, "startMediaPlayer: name = " + name);
        try {

            if (mMediaPlayer == null) {
                Log.i(TAG, "startMediaPlayer: mMediaPlayer == null");
                mMediaPlayer = new MediaPlayer();
            }

            try {
                //将MediaPlayer重置为未初始化状态,避免与底层native状态不一致导致IllegalStateException
                mMediaPlayer.reset();
                //传参为资源路径,例如D:/WorkSpace/ASProjects/test.mp3
                mMediaPlayer.setDataSource(DIR_VOICEPATH + name);
            } catch (Exception e) {
                Log.i(TAG, "startMediaPlayer reset:Exception --- " + e.toString());
                //若setDataSource过程产生异常,首先检测本地文件是否存在
                File file = new File(DIR_VOICEPATH + name);
                if (!file.exists()) {
                    Log.e(TAG, "startMediaPlayer: fileIsNotExists" );
                    checkCopyLocalWakeTTS(mContext);
                } else {
                    Log.i(TAG, "startMediaPlayer: fileExists");
                }
                //重置MediaPlayer对象,使新的MediaPlayer对象与native状态一致
                mMediaPlayer.release();
                mMediaPlayer = null;
                mMediaPlayer = new MediaPlayer();
                mMediaPlayer.setDataSource(DIR_VOICEPATH + name);
            }
            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

            //通过异步的方式装载媒体资源
            mMediaPlayer.prepareAsync();
            //为防止媒体资源未装载完直接调用start()报错,使用setOnPreparedListener事件待加载完后执行播放
            mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                    Log.i(TAG, "startMediaPlayer onPrepared: MediaPlayerStart");
                    //装载完成后执行播放
                    mMediaPlayer.start();
                }
            });

            mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    Log.d(TAG, "startMediaPlayer 本地唤醒音频播放完毕");
                        //播放完成后释放MediaPlayer
                        if (!mMediaPlayer.isPlaying()) {
                            Log.i(TAG, "startMediaPlayer onCompletion: isNotPlaying");
                            mMediaPlayer.release();
                            mMediaPlayer = null;
                        }
                    startASR();
                }
            });

            //使用reset()方法产生异常后回调此方法
            mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
                @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {
                    Log.i(TAG, "startMediaPlayer onError: ");
                    //本地唤醒音频问题导致异常后不应影响后续逻辑,启动识别
                    startASR();
                    return false;
                }
            });

        } catch (Exception e) {
            Log.e(TAG, "startMediaPlayer: Exception ---" + e.toString() );
            //本地唤醒音频问题导致异常后不应影响后续逻辑,启动识别
            startASR();
        }
    }

 

你可能感兴趣的:(Android:使用MediaPlayer 播放音频及android.media.MediaPlayer._setDataSource(Native Method)异常解决方法)