Мое приложение для Android (minSdkLevel: 2.2, targetSdkLevel: 3.0, тестирование на Nexus 7 с Android 4.2) на этапе загрузки выполняет следующие действия:
- Создает
SoundPool
и регистрирует на немOnLoadCompleteListener
- Начинает загружать звуки.
SoundPool
вызывает мой метод обратного вызова всякий раз, когда загружается звук. - Если процесс загрузки прерывается пользователем, вызывается
SoundPool.release()
- (Если
release()
уже был вызван и ранее начатая загрузка звука завершается, т.е. система вызывает мой обратный вызов, то мое приложение определяет, что мойSoundPool
уже являетсяnull
и игнорирует обратный вызов, так что это безопасно).
При тестировании на Nexus 7 через 210 мс после вызова SoundPool.release()
(logcat) произошла следующая ошибка:
т.е. порядок событий был, вероятно, следующим:
- Система начала асинхронно загружать звуки из-за моих
SoundPool.load(...)
звонков - Я вышел из экрана загрузки, поэтому был вызван
SoundPool.release()
, аSoundPool
было установлено наnull
. - (?) Система выполнила более ранний обратный вызов завершения загрузки звука, но обнаружила некоторую ошибку.
Я проверил исходный код Android, и код JNI для SoundPool.release()
действительно удаляет SoundPool
в JNI через DeleteGlobalRef
. Он использует слабую ссылку для хранения ссылки SoundPool
на уровне Java.
Я не могу воспроизвести ошибку, вероятно, потому, что не могу воспроизвести точные условия из-за недетерминизма. Система должна корректно обрабатывать возможность выпуска SoundPool
во время асинхронной загрузки звуков, но в некоторых редких случаях возникает ошибка. (Обратите внимание, что мой код выпускает SoundPool
ровно один раз после проверки на ноль, поэтому ошибка точно не в моем коде).
Любая идея о том, почему именно такая ошибка возникает в Android? Теоретически могут ли мои вышеприведенные подозрения быть правильными? Как я могу защитить свое приложение от этой ошибки Android? Может быть, я могу попробовать установить в логическом значении, что SoundPool
должен быть освобожден, дождаться возврата всех обратных вызовов, а затем освободить его (на основе этого логического значения) в последнем обратном вызове? Я не могу придумать никакого другого обходного пути, но я хотел бы быть уверен, что он, по крайней мере, сработает.