44

He subido mi aplicación en Google Play, pero los usuarios han informado de la siguiente excepciónjava.lang.RuntimeException: WakeLock bajo-bloqueado C2DM_LIB

java.lang.RuntimeException: WakeLock bajo-bloqueado C2DM_LIB. Esta excepción se produce cuando intento liberar el WakeLock. ¿Alguien puede decir cuál podría ser el problema?

Respuesta

51

He rastreado la misma excepción en la nueva biblioteca de GCM también. En realidad, la vieja biblioteca C2DM de Android tiene el mismo error, el mismo bloqueo, y Google aún no lo ha solucionado. Como puedo ver en nuestras estadísticas, alrededor del 0.1% de los usuarios experimentan este bloqueo.

Mis investigaciones muestran que el problema está en la liberación incorrecta de la red WakeLock en la biblioteca GCM, cuando la biblioteca intenta liberar WakeLock que no retiene nada (el contador de bloqueo interno se vuelve negativo).

Estaba satisfecho con una solución simple: simplemente tome esta excepción y no haga nada, porque no necesitamos hacer ningún trabajo adicional, entonces nuestro wakelock no tiene nada.

Para ello, debe importar las fuentes de la biblioteca de GCM en su proyecto, en lugar del archivo compilado .jar. Puede encontrar los orígenes de la biblioteca de GCM en la carpeta "$ Android_SDK_Home $/extras/google/gcm/gcm-client/src" (debe descargarla primero con el Administrador de Android SDK).

Siguiente abierta clase GCMBaseIntentService, encuentra la línea

sWakeLock.release(); 

y lo rodean con try-catch.

Se debe tener este aspecto:

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

ACTUALIZACIÓN: Alternativally, como se sugiere en @fasti his answer, puede utilizar mWakeLock.isHeld() método para comprobar si wakelock la tenencia efectiva de este bloqueo.

+0

¿Ha probado ..? Funciona bien después de rodearlo con try catch ... – Rookie

+1

Sí, he implementado esta solución en todos nuestros proyectos, funciona perfectamente (usuarios de más de 2 millones de usuarios) – HitOdessit

+0

ok, gracias ..... – Rookie

135

No ha publicado su código, por lo que no sé si ya ha hecho lo que voy a sugerir aquí, pero también tuve esa excepción y todo lo que agregué para arreglarlo era un simple "si" a asegúrese de que el WakeLock esté detenido, antes de intentar liberarlo.

Todo añadí en mi onPause era este "if" (antes de la "liberación()"):

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

y la excepción había desaparecido.

+6

Esta solución me parece mucho más limpia que la aceptada. – ottel142

+1

Eso es porque es -y- la forma correcta de hacerlo. Esta debería haber sido la respuesta aceptada. – ComputerEngineer88

+0

No tengo .release() en mi código (no mWakeLock), pero sigo recibiendo este error. La única stacktrace que veo es: java.lang.RuntimeException: WakeLock bloqueado por debajo de GCM_LIB en [...] com.google.android.gcm.GCMBaseIntentService.onHandleIntent (GCMBaseIntentService.java:252) en android.app. IntentService $ ServiceHandler.handleMessage (IntentService.java:65) – Ted

3

Aunque la solución isHeld() parece más agradable, en realidad puede fallar, porque no es atómica (es decir, no es segura para subprocesos). Si tiene más de un hilo que puede liberar el bloqueo, entonces entre el cheque (isHeld) y la llamada para transferir otro hilo puede liberar el bloqueo ... y luego falla.

Al usar try/catch, usted oculta el error, pero de una manera segura para la ejecución de subprocesos.

+0

¿Existe una buena opción para hacer que una versión WakeLock sea atómica de forma reutilizable? Debería ser una operación atómica. Literalmente tiene "Bloqueo" en el nombre. – colintheshots

1

No tengo este problema siempre que no reinicie el bloqueo de activación y llame a adquirir en el nuevo objeto. Solo debe mantener una instancia de wakeLock (así que conviértala en una variable de campo). Entonces sabes que siempre estás liberando ese wakeLock.

Así que ....

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