Odpowiedz

51

Wykreślam ten sam wyjątek w nowej bibliotece GCM. Właściwie stara biblioteka C2DM Android ma ten sam błąd, tę samą awarię, a Google jej jeszcze nie naprawiło. Jak wynika z naszych statystyk, około 0,1% użytkowników doświadcza tej awarii.

Moje badania pokazują, że problem polega na nieprawidłowym udostępnieniu sieci WakeLock w bibliotece GCM, gdy biblioteka próbuje zwolnić wartość WakeLock (wewnętrzny licznik blokowania staje się ujemny).

Byłem usatysfakcjonowany prostym rozwiązaniem - złap ten wyjątek i nie rób nic, ponieważ nie musimy wykonywać żadnej dodatkowej pracy, a nasz wakelock nic nie trzyma.

W tym celu należy zaimportować źródła biblioteki GCM do projektu, zamiast już skompilowanego pliku .jar. Źródła biblioteki GCM można znaleźć w folderze "$ Android_SDK_Home $/extras/google/gcm/gcm-client/src" (najpierw pobierz go za pomocą Menedżera Android SDK).

Następny otwarty GCMBaseIntentService klasa, znajdź linię

sWakeLock.release(); 

i otaczają go try-catch.

To powinno wyglądać tak:

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, jak sugeruje @fasti w his answer, można użyć mWakeLock.isHeld() sposób sprawdzić, czy blokada wybudzenia faktycznie trzyma tę blokadę.

+0

Czy próbowała go ..? Czy to działa dobrze po otworzeniu go z próbą catch .. – Rookie

+1

Tak, wdrożyłem to rozwiązanie we wszystkich naszych projektach, działa idealnie (baza użytkowników ponad 2M użytkowników) – HitOdessit

+0

ok, dzięki ..... – Rookie

135

Nie wysłałeś swojego kodu, więc nie wiem, czy już zrobiłeś to, co zasugeruję tutaj, , ale miałem też ten wyjątek i wszystko, co dodałem, aby naprawić, było proste "jeśli" do upewnij się, że WakeLock rzeczywiście jest trzymany, przed próbą zwolnienia go.

Wszystko Dodałem w moim OnPause był to "if" (przed wydaniem "()"):

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

i wyjątek zniknęła.

+6

To rozwiązanie wydaje mi się o wiele czystsze niż akceptowane. – ottel142

+1

To dlatego, że jest - i - we właściwy sposób to zrobić. To powinna być zaakceptowana odpowiedź. – ComputerEngineer88

+0

Nie mam .release() w moim kodzie (nie mWakeLock, co tak zawsze), ale nadal otrzymuję ten błąd. Wyświetlany jest tylko stacktrack: java.lang.RuntimeException: WakeLock under-locked GCM_LIB na [...] com.google.android.gcm.GCMBaseIntentService.onHandleIntent (GCMBaseIntentService.java:252) na android.app. IntentService $ ServiceHandler.handleMessage (IntentService.java:65) – Ted

3

Mimo że rozwiązanie isHeld() wydaje się być ładniejsze, może się nie powieść - ponieważ nie jest atomowe (to znaczy nie jest bezpieczne dla wątków). Jeśli masz więcej niż jeden wątek, który może zwolnić blokadę, to pomiędzy sprawdzeniem (isHeld) a wywołaniem ponownego uruchomienia innego wątku może zwolnić blokadę ... a następnie zawiedzie.

Za pomocą try/catch można ukryć błąd, ale w sposób wątkowy.

+0

Czy istnieje dobry sposób, aby dokonać atomowej emisji WakeLock w sposób wielokrotnego użytku? Powinna to być operacja atomowa. To dosłownie ma "Lock" w nazwie. – colintheshots

1

Nie mam tego problemu, o ile nie zainicjuję ponownie blokady wybudzania i nie otrzymam wywołania w nowym obiekcie. Powinieneś przechowywać tylko jedną instancję wakeLock (ustaw ją jako zmienną polową). Wtedy wiesz, że zawsze wypuszczasz ten jeden wakeLock.

Więc ....

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