Android MediaPlayer 空指针异常

5

这对我来说有点困难,因为我是通过别人的安卓设备崩溃报告获取的信息,我无法向他们提问,并且我自己的安卓设备从未出现过这种情况。

崩溃报告显示是在 Android 4.1.2 上发生,堆栈跟踪如下:

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)

很遗憾,grepcode.com上的Android源代码似乎与这些行号不匹配,因此我不确定哪个对象为空。我不知道用户在发生这种情况时正在做什么,所以我不知道是在播放音乐或声音效果时发生,还是在销毁时发生。我有以下代码在活动的onDestroy方法中:
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;
}

有什么事情我应该做吗?

(这段文本已翻译为中文)

2
你的代码中 curPlayers 代表什么意思? - GrIsHu
curPlayers是一个类级别的变量,包含当前媒体播放器列表的列表。我有一个声音效果系统,在可能的情况下使用现有的MediaPlayer,但对于需要在相同声音已经输出时播放的声音效果,会创建新的MediaPlayer。 - HappyEngineer
4个回答

5
在调用release()之前,请删除对MediaPlayer.stop()的调用。我们在Nexus 4、5、7、10和Moto X上看到了很多类似的崩溃。您可以在MediaPlayer$EventHandler.handleMessage中的NullPointerException中了解更多信息。
据我所知,他们在某个时候切换到从stop()发送消息,如果你运气不好,你的release()将在他们检查它是否为null并尝试调用其方法后立即将一个对象置为空。

2
汉克和德米特里的做法几乎正确,但最好采用综合方法。
在Android KitKat和Lollipop版本中,竞争条件存在于内部MediaPlayer事件处理程序和reset()/release()之间。 release()会与所有事件处理程序(例如onCompletion)产生竞争条件,而reset()仅会与播放状态消息产生竞争条件(start()、pause()、stop()、onCompletion()、onInfo()也会发布内部播放状态消息)。如果在空检查后但引用之前处理这些消息时调用reset()/release(),将导致NPE错误。
为了避免这种情况,您可以:
1.永远不要调用reset()或release()。这是不可接受的,因为每个MediaPlayer对象都必须释放。
2.仅从事件处理程序(例如onCompletion、onError等)中调用reset()或release()。这样可以避免竞争,但本身还是不可接受的,因为您可能需要在没有事件的情况下调用reset()/release()。
3.在事件之外调用reset()/release()时,如果媒体播放器未停止(例如正在播放或已暂停),请调用stop()以触发内部消息和已知的稳态,然后等待一段时间(例如50毫秒)再调用release()。这基本上是汉克的建议。
最好的方法是综合方法2和3。

1

不是

在调用 release() 之前,先移除对 MediaPlayer.stop() 的调用。

我在 release() 前添加了 Thread.sleep(50),并解决了这个异常。似乎不仅 stop() 会受到对 release() 的空值处理的影响,一些其他事件处理程序(如 OnCompletionListener)也存在此问题。


0

根据您的具体情况,我认为目标设备可能正在运行自制固件映像。

媒体播放器可能已经定制,但程序编写不正确。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接