回答

51

我也在新的GCM库中追踪了相同的异常。其实老C2DM Android库有相同的错误,同样的崩溃,谷歌还没有修复它。从我们的统计数据中可以看出,大约有0.1%的用户遇到这种崩溃。

我的调查显示,问题是在GCM库中的网络WakeLock的错误释放,当库试图释放不包含任何内容的WakeLock(内部锁定计数器变为负数)时。

我对简单的解决方案感到满意 - 只是赶上这个例外,什么也不做,因为我们不需要做任何额外的工作,那么我们的wakelock什么也不要。

为了做到这一点,您需要在您的项目中导入GCM库源代码,而不是已经编译好的.jar文件。您可以在“$ Android_SDK_Home $/extras/google/gcm/gcm-client/src”文件夹下找到GCM库源文件(您需要先使用Android SDK Manager下载它)。

下一页开放GCMBaseIntentService类,发现线

sWakeLock.release(); 

,并用的try-catch围绕着它。

它应该是这样的:

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,如建议@fasti在his answer,您可以使用mWakeLock.isHeld()的方法来检查,如果激活锁定实际持有这个锁。

+0

你已经尝试过了..?它是否运行良好后围绕它尝试赶上.. – Rookie

+1

是的,我已经在我们所有的项目中实施了这个解决方案,它完美的工作(用户数超过2M用户) – HitOdessit

+0

好的,谢谢..... – Rookie

135

你没有发布你的代码,所以我不知道你是否已经完成了我在这里的建议, 但我也有这个例外,我补充说,所有我修复它是一个简单的“如果”到确保WakeLock实际上正在举行之前,试图释放它

所有我在加的onPause是这样的: “如果” 的声明(以下简称 “发行()” 前):

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

和异常不见了。

+6

这个解决方案对我来说似乎比接受的更清洁。 – ottel142

+1

这是因为它是 - 而且 - 正确的做法。这应该是被接受的答案。 – ComputerEngineer88

+0

我没有.release()在我的代码(没有mWakeLock是如此),但我仍然得到这个错误。我看到的唯一stacktrace是: java.lang.RuntimeException:WakeLock未锁定GCM_LIB at com.google.android.gcm.GCMBaseIntentService.onHandleIntent(GCMBaseIntentService.java:252) at android.app。 IntentService $ ServiceHandler.handleMessage(IntentService.java:65) – Ted

3

尽管isHeld()解决方案看起来更好,但它实际上可能会失败 - 因为它不是原子的(即不是线程安全的)。如果你有多个可能释放锁的线程,那么在检查(isHeld)和另一个线程的调用之间可能会释放锁......然后你失败。

通过使用try/catch,你掩饰了这个错误,但是以一种线程安全的方式。

+0

有没有一个很好的选择,以可重用的方式使WakeLock发布原子?它应该是一个原子操作。它的名字实际上有“锁定”。 – colintheshots

1

我没有这个问题,只要我不重新初始化唤醒锁和调用获取新对象。你应该只保留一个wakeLock实例(所以把它作为一个字段变量)。然后你知道你总是发布那个wakeLock。

所以....

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