Problem introduction:

When writing a music player, I encountered an inexplicable problem, which was that when I switched songs in the playlist, the songs would twitch and switch several songs in the same way. At first it was thought to be caused by a sticky event of Livedata. But we ran it down, and it’s not the cause. Error (-38,0) was reported by the console.

Analysis of the

Initially I set OnCompletionListener only for MediaPlayer. The logic is as follows:

private MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { Log.d(TAG, "onCompletion: "); // Single loop if (playMode == TYPE_SINGLE) {playInner(); } else {playNextInner(); // Play next song}}};Copy the code

I’m going to add Log to it

Log.d(TAG, "onCompletion: ");
Copy the code

Sure enough, onCompletion execution in OnCompletionListener occurs when an Error occurs. The Error is as follows:

MediaPlayerNative: Attempt to call getDuration in wrong state: mPlayer=0x70d0559040, mCurrentState=4 error (-38, 0)
MediaPlayer: Error (-38,0)
Copy the code

So this is why the song was spooky when I cut it earlier.

why

At this point, I have a big question: why is onCompletion performed when error occurs? Shouldn’t it only be executed when the current song ends?

Look at the timing of onCompletion in the source code

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;Copy the code

If you look at the source code, not only is onCompletion called when the song is playing, but if an error occurs and your Mediaplay doesn’t set onErrorListener or if onError returns false in your onErrorListener, The onCompletion method is also called.

The solution

private MediaPlayer.OnErrorListener onErrorListener = new MediaPlayer.OnErrorListener() { @Override public boolean OnError (MediaPlayer mp, int what, int extra) { Otherwise, the onCompletion method in OnCompletionListener returns true; }};Copy the code