java.lang.RuntimeException: WakeLock блокира C2DM_LIB

Качих приложението си в Google Play, но потребителите съобщиха за следното изключение

java.lang.RuntimeException: WakeLock недостатъчно заключен C2DM_LIB. Това изключение възниква, когато се опитам да освободя WakeLock. Някой може ли да каже какъв може да е проблема.


person Rookie    schedule 27.08.2012    source източник


Отговори (4)


Проследих същото изключение и в новата библиотека на GCM. Всъщност старата C2DM библиотека на Android има същата грешка, същия срив и 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();

и го заобиколете с опит-улов.

Трябва да изглежда така:

    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");
        }
    }

АКТУАЛИЗАЦИЯ: Като алтернатива, както предложи @fasti в отговора му, можете да използвате метода mWakeLock.isHeld() за проверка ако wakelock действително държи това заключване.

person HitOdessit    schedule 27.08.2012
comment
Да, внедрих това решение във всички наши проекти, работи перфектно (потребителска база повече от 2 милиона потребители) - person HitOdessit; 27.08.2012
comment
Продължих и направих тази промяна във форк на репото на Google и я поставих в Github: github.com/ajlyon/gcm - person Avram Lyon; 20.11.2012
comment
страхотно, мога ли просто да заменя буркана на GCM Library с този ( github.com/ajlyon/gcm/blob/master/gcm-client/dist/gcm-src.jar ) и да продължите с коригираната грешка? актуализиран ли е с последната версия на GCM lib? - person Mario Lenci; 13.03.2013
comment
Това е стара тема, така че не съм сигурен дали има постоянен интерес, но това, което не разбирам, е как може да се случи това. AFAI може да види, това е възможно само ако намерението onHandle се извиква извън runIntentInService. Кое не трябва да се случва никога правилно? - person user210504; 04.04.2013
comment
Това решение е тежко и замита проблема под килима. Този, предложен от fasti по-долу, е правилният начин за справяне с него. - person twig; 11.08.2013
comment
A да, Catch Throwable, знакът за качествена Java - person HaMMeReD; 24.09.2014
comment
Улавянето и игнорирането на изключение почти никога не е добра практика. Особено когато, както в този случай, има проста булева проверка за обработка на случая на грешка (вижте отговора на fasti). - person howettl; 30.01.2015

Не сте публикували кода си, така че не знам дали вече сте направили това, което ще предложа тук, но аз също имах това изключение и всичко, което добавих, за да го поправя, беше просто „ако“, за да направя уверете се, че WakeLock действително е задържан, преди да се опитате да го освободите.

Всичко, което добавих в моя onPause, беше този израз "if" (преди "release()"):

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

и изключението изчезна.

person fasti    schedule 19.02.2013
comment
Това решение ми изглежда много по-чисто от приетото. - person ottel142; 13.06.2013
comment
Това е така, защото това е - и - правилният начин да го направите. Това трябваше да е приетият отговор. - person CompEng88; 26.11.2013
comment
Нямам .release() в кода си (няма mWakeLock какво толкова), но все още получавам тази грешка. Единственият stacktrace, който виждам, е: java.lang.RuntimeException: WakeLock under-locked GCM_LIB at [...]com.google.android.gcm.GCMBaseIntentService.onHandleIntent(GCMBaseIntentService.java:252) at android.app.IntentService$ServiceHandler .handleMessage(IntentService.java:65) - person Ted; 31.01.2014
comment
Съгласен съм, че това трябва да е приетият отговор, но не забравяйте да поставите горния код в синхронизирано изявление. Имах редки случаи, при които заключването за събуждане беше освободено от друга нишка между извикванията на isHeld и освобождаване. - person Emanuel Moecklin; 08.05.2014
comment
имате ли пример за синхронизирания код? Мисля, че най-безопасно е да се използват всичките 3 метода.. Catch Throwable, и isHeld, и Synchronized.. хаха, ако се повдигне изключение по време на изпълнение, това е скъпо.. пестете енергия от батерията, като първо проверите isHeld, това е по-ефективно, с няколко микросекунди, хаха. - person hamish; 13.07.2014
comment
Това решение не успя в моя случай, продължавам да получавам следи: crashes.to/s/9b51ed151b2 - person josue.0; 03.04.2018
comment
Това може да изглежда по-чисто, но приложението ви все пак може да се срине, ако системата освободи вашия wakelock поради времето, което изтича между извикванията isHeld() и release(). - person morepork; 10.09.2018
comment
не работи за мен на редкия телефон @Synchronized open fun stop() { if (wakeLock != null && wakeLock!!.isHeld()) { wakeLock!!.release() wakeLock = null } } - person Crag; 11.04.2019

Въпреки че решението isHeld() изглежда по-хубаво, то всъщност може да се провали - защото не е атомно (т.е. не е безопасно за нишки). Ако имате повече от една нишка, която може да освободи заключването, тогава между проверката (isHeld) и извикването за повторно пускане друга нишка може да освободи заключването... и тогава ще се провалите.

Използвайки try/catch, вие прикривате грешката, но по безопасен за нишката начин.

person Shoham    schedule 05.06.2014
comment
Има ли добър вариант да направите издание на WakeLock атомно по начин за многократна употреба? Това трябва да е атомна операция. Буквално има Lock в името. - person colintheshots; 12.02.2018
comment
Сигурен ли си? Разглеждайки изходния код на PowerManager.java, изглежда, че тези функции са синхронизирани. - person Nick; 08.11.2018

Нямам този проблем, стига да не инициализирам повторно заключването за събуждане и придобиването на повикване на новия обект. Трябва да запазите само един екземпляр на wakeLock (затова го направете променлива на поле). Тогава знаете, че винаги пускате този един wakeLock.

So....

 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();
person MobileMon    schedule 12.07.2014