It's a bit difficult for me to troubleshoot this because I'm getting this via a crash report from someone else's android device, I have no way to ask them questions, and I've never seen it happen on my own android devices.
The crash reports says it's Android 4.1.2 and the stack trace is:
java.lang.NullPointerException
at android.media.MediaPlayer$EventHandler.handleMessage(MediaPlayer.java:2102)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5021)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:556)
at dalvik.system.NativeStart.main(Native Method)
Unfortunately the android source code at grepcode.com doesn't seem to match up with those line numbers, so I'm not sure which thing is null.
I don't know what the user was doing when this happens, so I don't know if this happens while music or sound effects are playing or if it happens on destroy or what. I sort of suspect that it may happen during destroy. I have the following code in the activity's onDestroy method:
public void onDestroy() {
synchronized(curPlayers) {
for(List<MediaP> ms : curPlayers.values()) {
synchronized(ms) {
for(MediaP m : ms) {
synchronized(m) {
m.m.stop();
m.m.release();
}
}
}
}
curPlayers.clear();
}
}
private static class MediaP {
private MediaP(MediaPlayer m) {
this.m = m;
}
private MediaPlayer m;
private boolean wasPlaying = false;
}
Is there something in there that I should be doing?
Remove a call to MediaPlayer.stop()
before release()
. We have seen a lot of similar crashes on Nexus 4, 5, 7, 10 and Moto X. You can read more here NullPointerException in MediaPlayer$EventHandler.handleMessage
As far as I understand, at one point they switched to sending messages from stop()
, and if you get unlucky enough, your release()
will nullify an object right after they check that it is not null and try to call its method.
Hank and Dmitry have this almost right, but it's best to use a combined approach.
The race condition is between the internal MediaPlayer event handler and reset()/release() on Android KitKat and Lollipop releases. release() creates a race condition with all event handlers (e.g. onCompletion), while reset() only has a race with playback state messages. (start(), pause(), stop(), onCompletion(), onInfo() also post internal playback state messages). The NPE happens if reset()/release() is called while handling these message after the null checks but before the dereference.
To avoid this, you could:
The best approach is a combination of 2 and 3.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With