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的主要内容,如果未能解决你的问题,请参考以下文章