回答

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