關於使用AlarmManager的注意事項


關於使用AlarmManager的注意事項

.

最近在做一個需求:客戶端按照規定的時間間隔向服務端發送定位。一看到這個需求就想到了使用 AlarmManager 來實現。 AlarmManager 經常被用來執行定時任務,比如設置鬧鈴、發送心跳包等。也許有人會有疑問:為什么不能使用相同具有定時效果的 TimerHandler 呢?

其實答案非常簡單,相對於 Handler 來說,使用 sendEmptyMessageDelayed 方法是依賴於 Handler 所在的線程的,如果線程結束,就起不到定時任務的效果;而 AlarmManager 依賴的是 Android 系統的服務,具備喚醒機制。比起 Handler 也就更合適了。

而至於 Timer 可以精確地做到定時操作,但是相比於 AlarmManager 而言還是差了一截。同理,如果手機關屏后長時間不使用, CPU 就會進入休眠模式。這個使用如果使用 Timer 來執行定時任務就會失敗,因為 Timer 無法喚醒 CPU 。

所以,綜上所述,AlarmManager 就成為了最佳選擇。

SDK API < 19

一般情況下,使用 AlarmManager 來執行重復定時任務的代碼如下所示:

alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), TIME_INTERVAL, pendingIntent); 

或者

alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), TIME_INTERVAL, pendingIntent); 

setRepeating 該方法用於設置重復定時任務。

  • 第一個參數表示鬧鍾類型:一般為 AlarmManager.ELAPSED_REALTIME_WAKEUP 或者 AlarmManager.RTC_WAKEUP 。它們之間的區別就是前者是從手機開機后的時間,包含了手機睡眠時間;而后者使用的就是手機系統設置中的時間。所以如果設置為 AlarmManager.RTC_WAKEUP ,那么可以通過修改手機系統的時間來提前觸發定時事件。另外,對於相似的 AlarmManager.ELAPSED_REALTIMEAlarmManager.RTC 來說,它們不會喚醒 CPU 。所以使用的頻率較少;
  • 第二個參數表示任務首次執行時間:與第一個參數密切相關。第一個參數若為 AlarmManager.ELAPSED_REALTIME_WAKEUP ,那么當前時間就為 SystemClock.elapsedRealtime() ;若為 AlarmManager.RTC_WAKEUP ,那么當前時間就為 System.currentTimeMillis()
  • 第三個參數表示兩次執行的間隔時間:這個參數沒什么好講的,一般為常量;
  • 第四個參數表示對應的響應動作:一般都是去發送廣播,然后在廣播接收 onReceive(Context context, Intent intent) 中做相關操作。

至此,一切順利,暢通無阻。

SDK API >= 19 && SDK API < 23

當你寫好代碼、滿心歡喜地將程序跑在手機上的時候,傻眼了!你會發現在 Android 4.4 及以上版本的定時任務不是按照規定時間間隔來執行的。比如你設置了每隔 3 分鍾發出一個 HTTP 請求,結果你一看莫名其妙地變成了隔 5 分鍾發一次。

What the fuck?

 
what the fuck

然后你查閱 Android 官網中關於 Android 4.4 API 會看到如下幾句話:

 
Android 4.4 API

恍然大悟!原來是 Google 為了追求系統省電,所以“偷偷加工”了一下喚醒的時間間隔。但也正如上面官網中所說的那樣,如果在 Android 4.4 及以上的設備還要追求精准的鬧鍾定時任務,要使用 setExact() 方法。

所以,相應的代碼就變成了這樣:

// pendingIntent 為發送廣播 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent); } else { alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), TIME_INTERVAL, pendingIntent); } private BroadcastReceiver alarmReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // 重復定時任務 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, pendingIntent); } // to do something doSomething(); } }; 

當你寫好了“加強版”的 AlarmManager 之后,內心肯定無比小激動。這下總應該行了吧?運行一下,果然沒錯!在 Android 4.4 上的確按照規定的時間間隔在執行任務。哈哈,這下大功告成了!!!

SDK API >= 23

在 Android 4.4 上品嘗到勝利的甜頭后,你順便在 Android 6.0 的設備上測試了一下。結果。。。。。。你又 TMD 傻眼了!

 
What the fuck

發現在設備關屏靜止一段時間后, AlarmManager 又又又不能正常工作了。相必此時你連日狗的心都有了吧!強忍着淚水,再次打開 Android 官網中關於 Android 6.0 變更 ,發現在 Android 6.0 中引入了低電耗模式和應用待機模式。然后接着往下看 對低電耗模式和應用待機模式進行針對性優化 ,發現會有下面一段話:

 
Android 6.0 API

啊啊啊啊啊啊!之前在 Android 4.4 上能用的 setExact() 方法在 Android 6.0 上因為低電耗模式又不能正常使用了。但是,Google 又又又提供了新的方法 setExactAndAllowWhileIdle() 來解決在低電耗模式下的鬧鍾觸發。

所以,Attention!相關的代碼又被改寫為這樣:

// pendingIntent 為發送廣播 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent); } else { alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), TIME_INTERVAL, pendingIntent); } private BroadcastReceiver alarmReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // 重復定時任務 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, pendingIntent); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, pendingIntent); } // to do something doSomething(); } }; 

到了這里,總算是把因 Android 版本差異導致 AlarmManager 的“坑”填完了。這份代碼已經可以滿足日常的重復定時任務了。


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM