SoundPoolThread вызывает SIGSEGV через JNI ERROR, доступ к удаленной глобальной ссылке

Мое приложение для Android (minSdkLevel: 2.2, targetSdkLevel: 3.0, тестирование на Nexus 7 с Android 4.2) на этапе загрузки выполняет следующие действия:

  1. Создает SoundPool и регистрирует на нем OnLoadCompleteListener
  2. Начинает загружать звуки. SoundPool вызывает мой метод обратного вызова всякий раз, когда загружается звук.
  3. Если процесс загрузки прерывается пользователем, вызывается SoundPool.release()
  4. (Если release() уже был вызван и ранее начатая загрузка звука завершается, т.е. система вызывает мой обратный вызов, то мое приложение определяет, что мой SoundPool уже является null и игнорирует обратный вызов, так что это безопасно).

При тестировании на Nexus 7 через 210 мс после вызова SoundPool.release() (logcat) произошла следующая ошибка: logcat

т.е. порядок событий был, вероятно, следующим:

  1. Система начала асинхронно загружать звуки из-за моих SoundPool.load(...) звонков
  2. Я вышел из экрана загрузки, поэтому был вызван SoundPool.release(), а SoundPool было установлено на null.
  3. (?) Система выполнила более ранний обратный вызов завершения загрузки звука, но обнаружила некоторую ошибку.

Я проверил исходный код Android, и код JNI для SoundPool.release() действительно удаляет SoundPool в JNI через DeleteGlobalRef. Он использует слабую ссылку для хранения ссылки SoundPool на уровне Java.

Я не могу воспроизвести ошибку, вероятно, потому, что не могу воспроизвести точные условия из-за недетерминизма. Система должна корректно обрабатывать возможность выпуска SoundPool во время асинхронной загрузки звуков, но в некоторых редких случаях возникает ошибка. (Обратите внимание, что мой код выпускает SoundPool ровно один раз после проверки на ноль, поэтому ошибка точно не в моем коде).

Любая идея о том, почему именно такая ошибка возникает в Android? Теоретически могут ли мои вышеприведенные подозрения быть правильными? Как я могу защитить свое приложение от этой ошибки Android? Может быть, я могу попробовать установить в логическом значении, что SoundPool должен быть освобожден, дождаться возврата всех обратных вызовов, а затем освободить его (на основе этого логического значения) в последнем обратном вызове? Я не могу придумать никакого другого обходного пути, но я хотел бы быть уверен, что он, по крайней мере, сработает.


person Thomas Calc    schedule 18.11.2012    source источник
comment
Если у вас снова произойдет сбой, запишите полную трассировку стека (включая все материалы SIGSEGV) и предоставьте ее в проблеме на b.android.com.   -  person CommonsWare    schedule 18.11.2012
comment
О нет, мой плохой! Я просматривал только журнал моего приложения, отфильтрованный по TAG. SIGSEGV, вероятно, был более подробно описан в нефильтрованном логарифме, не так ли?   -  person Thomas Calc    schedule 18.11.2012
comment
Эм, может быть. Прошли годы с тех пор, как я столкнулся с одним, поэтому я забыл. Извиняюсь!   -  person CommonsWare    schedule 18.11.2012
comment
Вчера мне удалось восстановить нефильтрованный лог, но больше информации в нем не было. Кажется, это зависит от компонента, насколько он разговорчив в случае SIGSEGV. Например. OpenGL ES очень разговорчив.   -  person Thomas Calc    schedule 18.11.2012


Ответы (1)


Как вы сказали, это похоже на ошибку в 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, который может быть удален сборщиком мусора, поэтому при вызове уведомления он пытается сослаться на этот объект, и происходит сбой «доступ к удаленной глобальной ссылке».

person bain    schedule 09.03.2013