简体   繁体   中英

SoundPoolThread causing SIGSEGV via JNI ERROR "accessed deleted global reference"

My Android application (minSdkLevel: 2.2, targetSdkLevel: 3.0, testing on Nexus 7 with Android 4.2) does the following in the loading phase:

  1. Creates a SoundPool and registers an OnLoadCompleteListener on it
  2. Starts loading sounds. SoundPool calls my callback method whenever a sound is loaded.
  3. If the loading process is interrupted by user, SoundPool.release() is called
  4. (If release() was already called and a previously started sound loading completes, ie the system calls my callback, then my application detects that that my SoundPool is already null and ignores the callback, so it is safe).

Testing on a Nexus 7, the following error occured at 210ms after SoundPool.release() was called (logcat):日志猫

Ie the event order was the following probably:

  1. The system started to load sounds asynchronously due to my SoundPool.load(...) calls
  2. I left the loading screen, so SoundPool.release() was called and SoundPool set to null
  3. (?) The system completed an earlier sound loading completion callback, but encountered some error.

I checked the Android source code and the JNI code for SoundPool.release() indeed deletes the SoundPool in JNI via DeleteGlobalRef . It uses a weak reference to store the SoundPool reference on Java level.

I cannot reproduce the bug, probably because I can't reproduce the exact conditions due to nondeterminism. The system should correctly handle that a SoundPool might be released while it loads sounds asynchronously, but it seems under some rare circumstances, a bug appears. (Note that my code releases SoundPool exactly once after a nullcheck, so the error is not in my code for sure).

Any idea about why exactly does such an error occur in Android? Can my above suspicions be correct, in theory? How can I protect my application against this Android bug? Maybe I can try setting in a boolean that the SoundPool should be released, wait until all callbacks returned, and then release it (based on this boolean) in the last callback? I can't think of any other workaround, but I would like to be sure that it will work, at least.

As you say, this appears to be a bug in Android due to a race condition. As requested by user CommonsWare I opened a bug with the backtrace: http://code.google.com/p/android/issues/detail?id=53043

The offending code appears to be in Android's media/jni/soundpool/SoundPool.cpp which does:

void SoundPool::notify(SoundPoolEvent event)
{
    Mutex::Autolock lock(&mCallbackLock);
    if (mCallback != NULL) {
        mCallback(event, this, mUserData);
    }
}

Looks like mCallback is a Java object that can be deleted by the Garbage Collector, so when notify is called it tries to reference this object and the "accessed deleted global reference" crash happens.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM