SoundPoolThread通过JNI ERROR导致SIGSEGV“访问已删除的全局引用”

时间:2012-11-18 01:01:14

标签: java android java-native-interface soundpool

我的Android应用程序(minSdkLevel:2.2,targetSdkLevel:3.0,使用Android 4.2在Nexus 7上进行测试)在加载阶段执行以下操作:

  1. 创建SoundPool并在其上注册OnLoadCompleteListener
  2. 开始加载声音。只要加载声音,SoundPool就会调用我的回调方法。
  3. 如果加载过程被用户中断,则调用SoundPool.release()
  4. (如果已调用release()并且之前启动的声音加载完成,即系统调用我的回调,则我的应用程序检测到我的SoundPool已经null并忽略了回调,所以很安全)。
  5. 在Nexus 7上进行测试,在SoundPool.release()被调用(logcat)后的210ms发生以下错误: logcat

    即。事件顺序可能如下:

    1. 由于SoundPool.load(...)来电
    2. ,系统开始异步加载声音
    3. 我离开了加载屏幕,因此调用SoundPool.release()并将SoundPool设置为null
    4. (?)系统完成了较早的声音加载完成回调,但遇到了一些错误。
    5. 我检查了Android源代码,SoundPool.release()的JNI代码确实通过SoundPool删除了JNI中的DeleteGlobalRef。它使用弱引用来存储Java级别的SoundPool引用。

      我无法重现这个错误,可能是因为我无法重现由于不确定性导致的确切条件。系统应正确处理SoundPool可能在异步加载声音时释放,但在极少数情况下,似乎会出现错误。 (请注意,我的代码在nullcheck之后只发布一次SoundPool,因此错误不在我的代码中。)

      在Android中出现这种错误的原因是什么?理论上,我的上述怀疑是否正确?如何保护我的应用程序免受此Android错误的影响?也许我可以尝试设置一个布尔值SoundPool应该被释放,等待所有回调返回,然后在最后一个回调中释放它(基于这个布尔值)?我想不出任何其他的解决方法,但我想确保它至少可以工作。

1 个答案:

答案 0 :(得分:2)

正如您所说,由于竞争条件,这似乎是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对象,因此当调用notify时,它会尝试引用此对象并发生“已访问已删除的全局引用”崩溃。