Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

android MediaPlayer NullPointerException

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?

like image 812
HappyEngineer Avatar asked Aug 14 '13 05:08

HappyEngineer


2 Answers

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.

like image 150
Dmitry Avatar answered Oct 31 '22 10:10

Dmitry


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:

  1. never call reset() or release(). This is not acceptable as each MediaPlayer object must be released().
  2. only call reset() or release() from an event handler (e.g. onCompletion, onError, etc.). This avoids the race, but is not acceptable by itself as you may need to call reset()/release() outside of an event.
  3. When calling reset()/release() outside of an event, call stop() if the media player is not stopped (e.g. playing or paused) to trigger the internal message and a known steady state, then wait some e.g. 50msec before calling release(). This is basically Hank's suggestion.

The best approach is a combination of 2 and 3.

like image 2
Lajos Molnar Avatar answered Oct 31 '22 11:10

Lajos Molnar