我的 Android 应用程序(minSdkLevel:2.2,targetSdkLevel:3.0,在 Android 4.2 的 Nexus 7 上进行测试)在加载阶段执行以下操作:
- 创建一个
SoundPool
并在其上注册一个OnLoadCompleteListener
- 开始加载声音。每当加载声音时,
SoundPool
都会调用我的回调方法。 - 如果加载过程被用户中断,则会调用
SoundPool.release()
- (如果
release()
已被调用并且之前启动的声音加载完成,即系统调用我的回调,那么我的应用程序会检测到我的SoundPool
已经null
并忽略回调,因此是安全的)。
在 Nexus 7 上进行测试,调用 SoundPool.release()
后 210 毫秒时出现以下错误 (logcat):
即事件顺序可能如下:
- 由于我的
SoundPool.load(...)
调用,系统开始异步加载声音 - 我离开了加载屏幕,因此调用了
SoundPool.release()
并将SoundPool
设置为null
- (?)系统完成了较早的声音加载完成回调,但遇到了一些错误。
我检查了Android源代码,SoundPool.release()
的JNI代码确实通过DeleteGlobalRef
删除了JNI中的SoundPool
。它使用弱引用来存储 Java 级别的 SoundPool
引用。
我无法重现该错误,可能是因为由于不确定性而无法重现确切的条件。系统应该正确处理异步加载声音时可能会释放 SoundPool
的情况,但似乎在某些罕见的情况下会出现错误。 (请注意,我的代码在空检查后恰好释放 SoundPool
一次,因此错误肯定不在我的代码中)。
知道为什么 Android 中会发生这样的错误吗?从理论上讲,我的上述怀疑是否正确?如何保护我的应用程序免受此 Android 错误的影响?也许我可以尝试设置一个 boolean 值来释放 SoundPool
,等到所有回调返回,然后在最后一个回调中释放它(基于这个 boolean 值)?我想不出任何其他解决方法,但我想确保它至少会起作用。
最佳答案
正如您所说,由于竞争条件,这似乎是 Android 中的一个错误。根据用户 CommonsWare 的要求,我打开了一个带有回溯的错误:http://code.google.com/p/android/issues/detail?id=53043
有问题的代码似乎位于 Android 的 media/jni/soundpool/SoundPool.cpp 中,其作用是:
void SoundPool::notify(SoundPoolEvent event)
{
Mutex::Autolock lock(&mCallbackLock);
if (mCallback != NULL) {
mCallback(event, this, mUserData);
}
}
看起来 mCallback 是一个可以被垃圾收集器删除的 Java 对象,因此当调用 notification 时,它会尝试引用该对象,并且会发生“访问已删除的全局引用”崩溃。
关于java - SoundPoolThread 通过 JNI 导致 SIGSEGV 错误 "accessed deleted global reference",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13436470/