问题介绍:
在写一个音乐播放器时,遇到一个很莫名奇妙的问题,就是我在播放列表中切换歌曲的时候,歌曲会抽搐了一样的切换好几首。起初以为是Livedata的粘性事件引起的。可是排查了一下,并不是这个原因。于是怀疑是与控制台上报的 Error (-38,0) 的问题有关。
分析
一开始我只为MediaPlayer设置了OnCompletionListener,逻辑如下:
private MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
Log.d(TAG, "onCompletion: ");
//单曲循环
if (playMode == TYPE_SINGLE) {
playInner(); //继续播放当前的歌曲
} else {
playNextInner(); //播放下一首歌曲
}
}
};
我在其中加入Log
Log.d(TAG, "onCompletion: ");
果然当出现Error时OnCompletionListener中的onCompletion执行。Error如下:
MediaPlayerNative: Attempt to call getDuration in wrong state: mPlayer=0x70d0559040, mCurrentState=4 error (-38, 0)
MediaPlayer: Error (-38,0)
那么到这就知道为什么之前切歌的时候歌曲会鬼畜了。
原因
这时我有一个大大的疑问:为什么出现error的时候会执行 onCompletion 呢?不应该只有当前歌曲播放结束的时候才执行吗?
于是去查看源码中onCompletion的执行时机
case MEDIA_PLAYBACK_COMPLETE:
{
mOnCompletionInternalListener.onCompletion(mMediaPlayer);
OnCompletionListener onCompletionListener = mOnCompletionListener;
if (onCompletionListener != null)
onCompletionListener.onCompletion(mMediaPlayer);
}
stayAwake(false);
return;
case MEDIA_ERROR:
Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
boolean error_was_handled = false;
OnErrorListener onErrorListener = mOnErrorListener;
if (onErrorListener != null) {
error_was_handled = onErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2);
}
{
mOnCompletionInternalListener.onCompletion(mMediaPlayer);
OnCompletionListener onCompletionListener = mOnCompletionListener;
if (onCompletionListener != null && ! error_was_handled) {
onCompletionListener.onCompletion(mMediaPlayer);
}
}
stayAwake(false);
return;
查看源码发现,不仅只是歌曲播放完成时调用onCompletion,如果发生error并且你的Mediaplay并没有设置onErrorListener时 或者 你的OnErrorListener中的onError返回值为false,同样也会调用onCompletion方法。
解决方法
private MediaPlayer.OnErrorListener onErrorListener = new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
//你自己的逻辑
//此处返回值需要为true,不然还是会执行OnCompletionListener中的onCompletion方法
return true;
}
};