Android定時任務
AlarmManager
時隔將近1年多,部落格都沒有更新。實在是鴿了太久,都差點忘了CSDN的部落格。最近這一年的積累更多實在專案裡的點,相對於大塊的知識點來說,專案裡的點非常零碎,所以大篇幅的總結就比較少了。那麼現在把雲筆記的內容都搬上來和大家分享一下。
AlarmManager的用處
AlarmManger從名字就可以看出,這是一個鬧鐘管理器,就像是鬧鐘一樣可以設定。
在4.4系統之前,通過AlarmManager進行定時任務,時間進行的非常準確。而在4.4之後,為了降低功耗,系統檢測鬧鐘任務的頻率降低,使得AlarmManager執行時間並不準確,會出現5分鐘甚至更長的延時。
AlarmManager的設定
AlarmManager的關鍵點在於時間,任務以及執行方式。
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
設定時間
- AlarmManager.RTC:硬體鬧鐘,不喚醒手機(也可能是其它裝置)休眠;當手機休眠時不發射鬧鐘。
- AlarmManager.RTC_WAKEUP:硬體鬧鐘,當鬧鐘發生時喚醒手機休眠;
- AlarmManager.ELAPSED_REALTIME:真實時間流逝鬧鐘,不喚醒手機休眠;當手機休眠時不發射鬧鐘。
- AlarmManager.ELAPSED_REALTIME_WAKEUP:真實時間流逝鬧鐘,當鬧鐘發躰時喚醒手機休眠;
實際上大體上可以分為RCT類時間和REALTIME類時間
- RCT:裝置時間,也就是直接在手機標題欄中顯示的時間。手機內常用的鬧鐘使用的就是這一類時間
- REALTIME:cpu執行時間,這個時間由裝置開啟開始計算。舉個例子,REALTIME的10000L指的就是開機後10s的時間
這兩類時間分別有喚醒和非喚醒兩類。區別就在於在cpu休眠時是否喚醒cpu執行任務
設定任務
AlarmManager根據PendingIntent設定任務,可以說PendingIntent是AlarmManager的行動指揮,在AlarmManager中可以包含多個PendingIntent
廣播標誌,AlarmManager在到達指定的時間點時,可以通過PendingIntent執行響應。也就是說在指定時間點可以通過AlarmManager
啟動一個Activity,BroadCast或是一個Service,在PendingIntent中放置需要的資料,達到定時任務的目的
需要注意的是,PendingIntent有一個requestCode需要唯一值進行設定
Intent intent_broadcast = new Intent("android.intent.action.FluxRecord");
intent_broadcast.putExtra("time", time);
PendingIntent pi = PendingIntent.getBroadcast(this, i, intent_broadcast, PendingIntent.FLAG_UPDATE_CURRENT);
list.add(pi);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
} else {
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
}
執行方式
執行AlarmManager主要可分為兩類,一次性任務和重複任務。
- 重複任務:非精確重複型別(不需要精確的間隔時間,系統將整合所有應用中的非精確重複鬧鐘,儘量平衡任務時間,降低cpu佔用頻率)
alarmManager.setInexactRepeating();
alarmManager.setRepeating();
- 一次性任務:不同的系統版本,對於AlarmManager的喚醒機制存在不同的處理,版本越高越力求於降低cpu喚醒的頻率。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
} else {
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
}
JobService
Android 5.0提供的新的定時任務。不過和AlarmManager不同的是,並沒有提供定時間點的任務,並且它的執行時間也並不算精確。
在5.0之後,google更提倡使用JobService,使用它更方便系統對這一類任務統一管理,儘量集中在cpu休眠狀態下喚醒的時機。
啟動/取消JobService
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
JobScheduler jobSchedule = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
PersistableBundle bundle = new PersistableBundle();
bundle.putInt("id", 1);
JobInfo jobInfo = new JobInfo.Builder(1, new ComponentName(getPackageName(), CustomJobService.class.getName()))
.setExtras(bundle) //傳遞資料
.setMinimumLatency(1000) //延遲執行時間
.setOverrideDeadline(1000) //最長延遲執行時間
.setPeriodic(1000) //重複執行週期間隔
.setPersisted(true) //需要android.permission.RECEIVE_BOOT_COMPLETED許可權,並且不可與setMinimumLatency,setOverrideDeadline混用
.setBackoffCriteria(1000, JobInfo.BACKOFF_POLICY_EXPONENTIAL) //任務失敗重試機制(間隔時間,重試機制)
.build();
int schedule = jobSchedule.schedule(jobInfo);
if (schedule >= 0) {
//任務正確執行
}
//取消任務
//jobSchedule.cancel(jobId);
//jobSchedule.cancelAll();
}
JobService任務處理
onStartJob()的返回值。
返回false時,任務處理完成後完全交給系統處理。在onStartJob中邏輯完成後,將會自動結束任務,而此時系統將不會呼叫onStopJob()。
返回true時,任務處理完成後需要我們自行結束任務,否則應用的其他任務都不會執行,直到這一任務結束。在呼叫finishJob()後,任務將會結束並且呼叫onStopJob()方法。
JobService將會在主執行緒中執行,因此存在多執行緒處理時,我們會需要將onStartJob()返回值設為true,自己決定任務結束的時間,防止在onStartJob()中邏輯完成後Service直接結束。(例如需要Handle來處理邏輯時,返回true可以保證Handle部分程式碼完整的執行)
public class CustomJobService extends JobService {
@Override
//任務執行時呼叫
public boolean onStartJob(JobParameters jobParameters) {
return false;
}
@Override
public boolean onStopJob(JobParameters jobParameters) {
return false;
}
}
AndroidMainfest中註冊
<service
android:name=".CustomJobService"
android:permission="android.permission.BIND_JOB_SERVICE" />
總結
關於AlarmManager的研究還是不夠深入,事實上在使用過程中AlarmManager在不同廠商不同機型下的表現不太相同,在部分機型下甚至有失效的情況出現(例如OPPOR9就出現過多次失效和延時過長),猜想是系統定製的時候有部分修改,但沒有做分析所以我無法下定論。在試圖用AlarmManager執行精確時間點任務時,4.4之前的版本執行可以沒有太大問題,可以保證部分機型成功,當然部分機型還是存在問題。而在4.4之後的版本,普遍存在任務延遲甚至失效的情況,在之後有時間會研究5.0之後的鬧鐘原始碼尋求解決方式。