Java.lang.RuntimeException: WakeLock не заблокирован C2DM_LIB

Я загрузил свое приложение в Google Play, но пользователи сообщили следующее исключение

java.lang.RuntimeException: WakeLock не заблокирован C2DM_LIB. Это исключение возникает, когда я пытаюсь освободить WakeLock. Может кто-нибудь сказать, что может быть проблемой.

Ответ 1

Я также проследил одно и то же исключение в новой библиотеке GCM. На самом деле у старой библиотеки Android C2DM есть такая же ошибка, такая же ошибка, и Google еще не исправил ее. Как я вижу по нашей статистике, около 0,1% пользователей испытывают этот крах.

Мои исследования показывают, что проблема заключается в некорректном выпуске сети WakeLock в библиотеке GCM, когда библиотека пытается освободить WakeLock, которая ничего не держит (внутренний счетчик блокировки становится отрицательным).

Я был доволен простым решением - просто поймайте это исключение и ничего не сделайте, потому что нам не нужно делать какую-либо дополнительную работу, тогда наш wakelock ничего не держит.

Для этого вам нужно импортировать источники библиотеки GCM в свой проект, а не уже скомпилированный файл .jar. Источники библиотеки GCM можно найти в папке "$ Android_SDK_Home $/extras/google/gcm/gcm-client/src" (сначала вам нужно загрузить ее с помощью Android SDK Manager).

Далее откройте класс GCMBaseIntentService, найдите строку

sWakeLock.release();

и объедините его с помощью try-catch.

Он должен выглядеть так:

    synchronized (LOCK) {
        // sanity check for null as this is a public method
        if (sWakeLock != null) {
            Log.v(TAG, "Releasing wakelock");
            try {
                sWakeLock.release();
            } catch (Throwable th) {
                // ignoring this exception, probably wakeLock was already released
            }
        } else {
            // should never happen during normal workflow
            Log.e(TAG, "Wakelock reference is null");
        }
    }

UPDATE: Альтернативно, как было предложено @fasti в его ответе, вы можете использовать метод mWakeLock.isHeld(), чтобы проверить, действительно ли wakelock держит эту блокировку.

Ответ 2

Вы не опубликовали свой код, поэтому я не знаю, уже ли вы сделали то, что я предложу здесь, но у меня также было это исключение, и все, что я добавил, чтобы установить, было просто "если" до убедиться, что WakeLock фактически удерживается, прежде чем пытаться его освободить.

Все, что я добавил в мой onPause, было это утверждение "if" (до "release()" ):

if (mWakeLock.isHeld())
    mWakeLock.release();

и исключение прошло.

Ответ 3

Хотя решение isHeld() кажется более приятным, оно может фактически потерпеть неудачу, потому что оно не является атомарным (то есть не потокобезопасным). Если у вас есть несколько потоков, которые могут освободить блокировку, то между проверкой (isHeld) и вызовом для передачи другого потока может быть освобожден блокировка... а затем вы не выполняете.

Используя try/catch, вы маскируете ошибку, но потокобезопасным способом.

Ответ 4

У меня нет этой проблемы, если я не повторно инициализую блокировку слежения и вызову на новый объект. Вы должны сохранить только один экземпляр wakeLock (так что сделайте его полевой переменной). Тогда вы знаете, что вы всегда выпускаете этот один wakeLock.

Так....

 if (mWakeLock == null) {
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP
                | PowerManager.ON_AFTER_RELEASE, "MyWakeLock");
    }

try{
        mWakeLock.release();//always release before acquiring for safety just in case
    }
    catch(Exception e){
        //probably already released
        Log.e(TAG, e.getMessage());
    }
    mWakeLock.acquire();