44

Ich habe meine Anwendung auf Google Play hochgeladen aber die Benutzer berichtet haben die folgende Ausnahmejava.lang.RuntimeException: WakeLock Unter gesperrt C2DM_LIB

java.lang.RuntimeException: WakeLock Unter gesperrt C2DM_LIB. Diese Ausnahme tritt auf, wenn ich versuche, das WakeLock freizugeben. Kann mir jemand sagen, was das Problem sein könnte.

Antwort

51

Ich habe dieselbe Ausnahme in der neuen GCM-Bibliothek auch verfolgt. Eigentlich alte C2DM Android-Bibliothek haben den gleichen Fehler, der gleiche Absturz, und Google hat es noch nicht behoben. Wie ich in unseren Statistiken sehen kann, haben etwa 0,1% der Benutzer diesen Absturz erlebt.

Meine Untersuchungen zeigen, dass das Problem in der falschen Freigabe des Netzwerks WakeLock in der GCM-Bibliothek liegt, wenn die Bibliothek versucht, WakeLock freizugeben, die nichts enthält (interner Sperrzähler wird negativ).

Ich war mit einer einfachen Lösung zufrieden - fangen Sie einfach diese Ausnahme und nichts tun, weil wir keine zusätzliche Arbeit machen müssen, dann unsere Wakelock halten nichts.

Um dies zu tun, müssen Sie GCM-Bibliotheksquellen in Ihr Projekt importieren, anstatt bereits kompilierte .jar Datei. Sie können die Quellen der GCM-Bibliothek unter dem Ordner "$ Android_SDK_Home $/extras/google/gcm/gcm-client/src" finden (Sie müssen sie zuerst mit dem Android SDK Manager herunterladen).

Weiter offen GCMBaseIntentService Klasse finden Linie

sWakeLock.release(); 

und umgeben sie mit try-catch.

Es sollte wie folgt aussehen:

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: Alternativally, wie vorgeschlagen @fasti in his answer, Sie mWakeLock.isHeld() Methode können Sie prüfen, ob WakeLock tatsächlich diese Sperre hält.

+0

Haben Sie versucht, es ..? Es läuft gut nach dem Umgeben mit Versuch fangen. – Rookie

+1

Ja, ich habe diese Lösung in allen unseren Projekten implementiert, es funktioniert perfekt (Userbase mehr als 2M Benutzer) – HitOdessit

+0

ok, danke ..... – Rookie

135

Sie haben Ihren Code nicht veröffentlicht, also weiß ich nicht, ob Sie bereits getan haben, was ich hier vorschlagen werde, , aber ich hatte auch diese Ausnahme und alles, was ich hinzugefügt habe, um es zu reparieren, war ein einfaches "wenn" zu stellen Sie sicher, dass der WakeLock tatsächlich gehalten wird, bevor Sie versuchen, es freizugeben.

Alles, was ich in meinem onPause hinzugefügt wurde diese "if" Anweisung (vor dem "Release()"):

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

und die Ausnahme war verschwunden.

+6

Diese Lösung scheint mir viel sauberer als die akzeptierte Lösung. – ottel142

+1

Das liegt daran, dass es der richtige Weg ist. Dies sollte die akzeptierte Antwort sein. – ComputerEngineer88

+0

Ich habe keine .release() in meinem Code (kein mWakeLock was auch immer) aber ich bekomme immer noch diesen Fehler. Das einzige Stacktrace, das ich sehe, ist: java.lang.RuntimeException: WakeLock unter-locked GCM_LIB bei [...] com.google.android.gcm.GCMBaseIntentService.onHandleIntent (GCMBaseIntentService.java:252) bei android.app. IntentService $ ServiceHandler.handleMessage (IntentService.java:65) – Ted

3

Obwohl die isHeld() - Lösung schöner scheint, kann sie tatsächlich fehlschlagen - weil sie nicht atomar ist (d. H. Nicht threadsicher). Wenn Sie mehr als einen Thread haben, der die Sperre freigeben kann, dann kann zwischen der Überprüfung (isHeld) und dem Aufruf der Freigabe ein anderer Thread die Sperre freigeben ... und dann scheitern Sie.

Mit try/catch verkleiden Sie den Fehler, aber auf thread-sichere Weise.

+0

Gibt es eine gute Option, um eine WakeLock-Freigabe auf wiederverwendbare Weise atomar zu machen? Es sollte eine atomare Operation sein. Es hat buchstäblich "Lock" im Namen. – colintheshots

1

Ich habe dieses Problem nicht, solange ich die Wake-Sperre nicht neu initialisieren und Call auf dem neuen Objekt aufrufen. Sie sollten nur eine Instanz von wakeLock behalten (also eine Feldvariable machen). Dann weißt du, dass du immer das eine wakeLock veröffentlichst.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();