2

我的 Android 应用程序(minSdkLevel: 2.2, targetSdkLevel: 3.0, testing on Nexus 7 with Android 4.2)在加载阶段执行以下操作:

  1. 创建一个并在其上SoundPool注册一个OnLoadCompleteListener
  2. 开始加载声音。SoundPool每当加载声音时调用我的回调方法。
  3. 如果加载过程被用户中断,SoundPool.release()则调用
  4. (如果release()已经调用并且之前开始的声音加载完成,即系统调用我的回调,那么我的应用程序检测到我SoundPool的已经是null并忽略回调,所以它是安全的)。

在 Nexus 7 上进行测试,调用后 210 毫秒出现以下错误SoundPool.release()(logcat): 日志猫

即事件顺序可能如下:

  1. 由于我的SoundPool.load(...)调用,系统开始异步加载声音
  2. 我离开了加载屏幕,所以SoundPool.release()被调用并SoundPool设置为null
  3. (?) 系统完成了较早的声音加载完成回调,但遇到了一些错误。

我检查了 Android 源代码和 JNI 代码SoundPool.release()确实删除了SoundPoolJNI 中的DeleteGlobalRef. 它使用弱引用来存储SoundPoolJava 级别的引用。

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

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

4

1 回答 1

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 时,它会尝试引用该对象,并且会发生“访问已删除的全局引用”崩溃。

于 2013-03-09T20:57:00.550 回答