web-dev-qa-db-fra.com

java.lang.RuntimeException: WakeLock sous-verrouillé C2DM_LIB

J'ai téléchargé mon application sur Google Play, mais les utilisateurs ont signalé l'exception suivante

Java.lang.RuntimeException: WakeLock sous-verrouillé C2DM_LIB. Cette exception se produit lorsque j'essaie de libérer le WakeLock. Quelqu'un peut-il dire quel pourrait être le problème?.

49
Rookie

J'ai également tracé la même exception dans la nouvelle bibliothèque GCM. En fait, la vieille bibliothèque C2DM Android a la même erreur, le même plantage, et Google ne l'a pas encore corrigé. Comme je peux le voir par nos statistiques, environ 0,1% des utilisateurs connaissent ce plantage.

Mes investigations montrent que le problème est lié à une libération incorrecte du réseau WakeLock dans la bibliothèque GCM, lorsque la bibliothèque tente de libérer WakeLock qui ne contient rien (le compteur de verrouillage interne devient négatif).

J'étais satisfait d'une solution simple - il suffit de saisir cette exception et de ne rien faire, car nous n'avons pas besoin de faire de travail supplémentaire, alors notre wakelock ne contient rien.

Pour ce faire, vous devez importer les sources de bibliothèque GCM dans votre projet, plutôt que le fichier .jar Déjà compilé. Vous pouvez trouver les sources de la bibliothèque GCM sous le dossier " $ Android_SDK_Home $/extras/google/gcm/gcm-client/src" (vous devez d'abord le télécharger à l'aide du Android SDK Directeur).

Ouvrez ensuite la classe GCMBaseIntentService, recherchez la ligne

sWakeLock.release();

et l'entourer de try-catch.

Ça devrait ressembler à ça:

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

MISE À JOUR: Alternativement, comme suggéré @fasti dans sa réponse , vous pouvez utiliser la méthode mWakeLock.isHeld() pour vérifier si wakelock tient réellement ce verrou.

54
HitOdessit

Vous n'avez pas posté votre code, donc je ne sais pas si vous avez déjà fait ce que je proposerai ici, mais j'ai également eu cette exception et tout ce que j'ai ajouté pour le corriger était un simple "si" à assurez-vous que le WakeLock est réellement maintenu, avant d'essayer de le libérer.

Tout ce que j'ai ajouté dans ma onPause était cette instruction "if" (avant la "release ()"):

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

et l'exception avait disparu.

157
fasti

Bien que la solution isHeld () semble plus agréable, elle peut en fait échouer - car elle n'est pas atomique (c'est-à-dire non sécurisée pour les threads). Si vous avez plus d'un thread qui pourrait libérer le verrou, entre la vérification (isHeld) et l'appel à relâcher un autre thread peut libérer le verrou ... et vous échouez.

En utilisant try/catch, vous masquez le bogue, mais de manière thread-safe.

4
Shoham

Je n'ai pas ce problème tant que je ne réinitialise pas le verrou de réveil et n'appelons acquérir sur le nouvel objet. Vous ne devez conserver qu'une seule instance de wakeLock (alors faites-en une variable de champ). Ensuite, vous savez que vous libérez toujours ce wakeLock.

Alors....

 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();
1
MobileMon