1. 程式人生 > >Android定時任務

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之後的鬧鐘原始碼尋求解決方式。