java.lang.RuntimeException:WakeLock 未锁定 C2DM_LIB

Posted

技术标签:

【中文标题】java.lang.RuntimeException:WakeLock 未锁定 C2DM_LIB【英文标题】:java.lang.RuntimeException: WakeLock under-locked C2DM_LIB 【发布时间】:2012-08-21 20:58:32 【问题描述】:

我已在 google play 上上传了我的应用程序,但用户报告了以下异常

。当我尝试释放WakeLock 时会发生此异常。谁能告诉我可能是什么问题。

【问题讨论】:

【参考方案1】:

你没有发布你的代码,所以我不知道你是否已经完成了我在这里建议的操作, 但我也遇到了这个异常,我添加的所有修复它只是一个简单的“如果”,以确保在尝试释放 WakeLock 之前确实被持有

我在 onPause 中添加的只是这个“if”语句(在“release()”之前):

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

异常消失了。

【讨论】:

这个解决方案对我来说似乎比接受的解决方案干净得多。 那是因为它是正确的方法。这应该是公认的答案。 我的代码中没有 .release() (没有 mWakeLock 什么的),但我仍然收到此错误。我看到的唯一堆栈跟踪是:java.lang.RuntimeException: WakeLock under-locked GCM_LIB at [...]com.google.android.gcm.GCMBaseIntentService.onHandleIntent(GCMBaseIntentService.java:252) at android.app.IntentService$ServiceHandler .handleMessage(IntentService.java:65) 我同意这应该是公认的答案,但不要忘记将上面的代码放在同步语句中。在调用 isHeld 和 release 之间,唤醒锁被另一个线程释放的情况很少见。 你有同步代码的例子吗?我认为最安全的方法是使用所有 3 种方法.. Catch Throwable, and isHeld, and Synchronized.. 哈哈如果在运行时引发异常,它很昂贵.. 通过首先检查 isHeld 来节省电池电量,它更有效,几微秒,哈哈。【参考方案2】:

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

我的调查表明问题在于 GCM 库中网络 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");
        
    

更新: 或者,按照his answer 中的@fasti 建议,您可以使用mWakeLock.isHeld() 方法检查wakelock 是否实际持有此锁。

【讨论】:

是的,我已经在我们所有的项目中实施了这个解决方案,它运行良好(用户群超过 200 万用户) 我继续在 Google 的 repo 的一个分支中进行了这项更改,并将其放在 Github 上:github.com/ajlyon/gcm 酷,我可以用这个 (github.com/ajlyon/gcm/blob/master/gcm-client/dist/gcm-src.jar) 替换 GCM 库 jar 并继续修复该错误吗?是否使用 GCM 库的最新版本进行了更新? 这是一个旧线程,所以我不确定是否有任何持续的兴趣,但我不明白这怎么会发生。 AFAI 可以看到,这只有在从 runIntentInService 外部调用 onHandle 意图时才有可能。哪一个不应该发生? 这个解决方案是笨拙的,可以将问题扫到地毯下。下面fasti提出的就是正确的处理方式。【参考方案3】:

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

通过使用 try/catch,您可以伪装错误,但以线程安全的方式。

【讨论】:

以可重用的方式使 WakeLock 释放原子有一个好的选择吗?它应该是一个原子操作。它的名字中确实有“锁”。 你确定吗?看PowerManager.java源码,貌似这些函数是同步的。【参考方案4】:

只要我不重新初始化唤醒锁并在新对象上调用acquire,我就没有这个问题。您应该只保留一个 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();

【讨论】:

以上是关于java.lang.RuntimeException:WakeLock 未锁定 C2DM_LIB的主要内容,如果未能解决你的问题,请参考以下文章

Android Studio 按钮导致崩溃

添加 ImageView 时应用程序崩溃?