一、長(zhǎng)時(shí)間定時(shí)任務(wù),比較Timer、Hnadler、AlarmManager
Timer,有一個(gè)明顯的問(wèn)題,它并不太適合用于需要長(zhǎng)期在后臺(tái)運(yùn)行的定時(shí)任務(wù)。我們都知道,為了能讓電池更加耐用,每種手機(jī)都會(huì)有自己的休眠策略,Android手機(jī)就會(huì)在長(zhǎng)時(shí)間不操作的情況下自動(dòng)讓CPU進(jìn)入睡眠狀態(tài),這就有可能導(dǎo)致Timer中的定時(shí)任務(wù)無(wú)法正常運(yùn)行。
Hnadler,Handler的postDelay方法也可以實(shí)現(xiàn)定時(shí)操作,它同樣也是不靠譜的,因?yàn)槟J(rèn)Hnadler依賴于線程(main線程或者子線程),所以只要進(jìn)程被殺死,所有相關(guān)的線程都被曬死,所以handler中的定時(shí)操作就無(wú)效了。Timer也一樣,因?yàn)門imer實(shí)際上是另起一個(gè)子線程,進(jìn)程被殺,子線程當(dāng)然也被殺了。
AlarmManager,它通過(guò)pendingIntent具有喚醒未啟動(dòng)進(jìn)程的功能,即可以保證每次需要執(zhí)行定時(shí)任務(wù)的時(shí)候CPU都能正常工作。但是當(dāng)設(shè)備關(guān)機(jī)和重啟后,鬧鐘將被清除。
二、使用AlarmManager
通過(guò)上面的分析,我們可以明顯感受到AlarmManager在長(zhǎng)時(shí)間定時(shí)任務(wù)中的優(yōu)點(diǎn),下面來(lái)說(shuō)說(shuō)它的用法(由于AlarmManager在不同的Android版本都有修改,所以如果想使用AlarmManager需要處理不同版本問(wèn)題)
1. 初始化PendingIntent
Intent intent = new Intent(ALARM_ACTION_CODE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(),
REQUEST_CODE, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
PendingInent,Intent 更加傾向于去立即執(zhí)行某個(gè)動(dòng)作,而 PendingIntent 更加傾向于在某個(gè)合適的時(shí)機(jī)去執(zhí)行某個(gè)動(dòng)作。所以,也可以把 PendingIntent 簡(jiǎn)單地理解為延遲執(zhí)行的 Intent。
getActivity()方法、getBroadcast()方法、getService()方法ALARM_ACTION_CODE,Intent傳遞給廣播接收者的action,自己設(shè)定。
-
PendingIntent.FLAG_CANCEL_CURRENT,pendingIntent的第四個(gè)參數(shù)如果直接傳0,表示你不打算通過(guò)任何一個(gè)flag來(lái)控制pendingIntent的創(chuàng)建。下面說(shuō)說(shuō)pendingIntent提供的四種flag:
- FLAG_CANCEL_CURRENT,如果要?jiǎng)?chuàng)建的PendingIntent已經(jīng)存在了,那么在創(chuàng)建新的PendingIntent之前,原先已經(jīng)存在的PendingIntent中的intent將不能使用。
- FLAG_NO_CREATE,如果要?jiǎng)?chuàng)建的PendingIntent尚未存在,則不創(chuàng)建新的PendingIntent,直接返回null。
- FLAG_ONE_SHOT,相同的PendingIntent只能使用一次,且遇到相同的PendingIntent時(shí)不會(huì)去更新PendingIntent中封裝的Intent的extra部分的內(nèi)容。
- FLAG_UPDATE_CURRENT,如果要?jiǎng)?chuàng)建的PendingIntent已經(jīng)存在了,那么在保留原先PendingIntent的同時(shí),將原先PendingIntent封裝的Intent中的extra部分替換為現(xiàn)在新創(chuàng)建的PendingIntent的intent中extra的內(nèi)容。
那么如何比較新老pendingIntent是否相同呢?
PendingIntent重寫了equals方法,判定兩個(gè)PendingIntent是否相同的依據(jù)是它封裝的Intent是否“相同”和requestCode是否一致。注意,“相同”上打了引號(hào),這是因?yàn)樵诒容^PendingIntent中封裝的intent時(shí)是否相同時(shí),使用的是Intent的filterEquals方法,該方法認(rèn)為只要兩個(gè)intent具有相同的action、data、categories、components、type和flags(這個(gè)flags是intent的flags)就認(rèn)為它們兩個(gè)是“相同”的,filterEquals是不會(huì)比較兩個(gè)intent的extra部分和內(nèi)存地址的。
另外需要注意的是,Intent并沒有重寫equals方法,所以如果使用Intent的equals方法比較兩個(gè)intent對(duì)象的話,比較的是兩個(gè)對(duì)象的內(nèi)存地址。
2. 初始化AlarmManager
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
3. 設(shè)置重復(fù)執(zhí)行的定時(shí)任務(wù)
這個(gè)就要區(qū)分Android版本了
4.4之前,SDK API < 19
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), TIME_INTERVAL, pendingIntent);
或
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis(), TIME_INTERVAL, pendingIntent);
- 第一個(gè)參數(shù)表示鬧鐘類型:一般為
AlarmManager.ELAPSED_REALTIME_WAKEUP或者AlarmManager.RTC_WAKEUP。它們之間的區(qū)別就是前者是從手機(jī)開機(jī)后的時(shí)間,包含了手機(jī)睡眠時(shí)間;而后者使用的就是手機(jī)系統(tǒng)設(shè)置中的時(shí)間。所以如果設(shè)置為AlarmManager.RTC_WAKEUP,那么可以通過(guò)修改手機(jī)系統(tǒng)的時(shí)間來(lái)提前觸發(fā)定時(shí)事件。另外,對(duì)于相似的AlarmManager.ELAPSED_REALTIME和AlarmManager.RTC來(lái)說(shuō),它們不會(huì)喚醒 CPU 。所以使用的頻率較少; - 第二個(gè)參數(shù)表示任務(wù)首次執(zhí)行時(shí)間:與第一個(gè)參數(shù)密切相關(guān)。第一個(gè)參數(shù)若為
AlarmManager.ELAPSED_REALTIME_WAKEUP,那么當(dāng)前時(shí)間就為SystemClock.elapsedRealtime();若為AlarmManager.RTC_WAKEUP,那么當(dāng)前時(shí)間就為System.currentTimeMillis(); - 第三個(gè)參數(shù)表示兩次執(zhí)行的間隔時(shí)間:這個(gè)參數(shù)沒什么好講的,一般為常量;
- 第四個(gè)參數(shù)表示對(duì)應(yīng)的響應(yīng)動(dòng)作:一般都是去發(fā)送廣播,然后在廣播接收
onReceive(Context context, Intent intent)中做相關(guān)操作。
4.4之后,6.0之前, SDK API >= 19, SDK API < 23
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), pendingIntent);
查閱 Android 官網(wǎng)中關(guān)于 Android 4.4 API 會(huì)看到如下幾句話:

6.0之后的版本,SDK API >= 23
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), pendingIntent);
再次打開 Android 官網(wǎng)中關(guān)于 Android 6.0 變更 ,發(fā)現(xiàn)在 Android 6.0 中引入了低電耗模式和應(yīng)用待機(jī)模式。然后接著往下看 對(duì)低電耗模式和應(yīng)用待機(jī)模式進(jìn)行針對(duì)性優(yōu)化 ,發(fā)現(xiàn)會(huì)有下面一段話:
