Android 7.0之JobScheduler 分析(一)——如何使用job

背景

最近公司好多人都因爲Jobscheduler的使用不當導致各種問題,Job定時任務不生效或者衝突。歸根結底是對Jobscheduler的使用不熟悉以及,其工作原理沒有一個系統性的瞭解。本人也曾踩坑,所以下定決心好好熟悉Jobscheduler。

概述

   在android開發中經常會有這樣的需求,開發者需要在稍後的某個時間點或者滿足某個特定的條件時去執行某個任務,例如當設備開始充電,或者網絡狀態連接到wifi狀態時執行某些推送通知的任務,jobscheduler就是用來處理這類場景的任務。
  Jobscheduler的android在5.0上針對於降低功耗而提出來的一種策略方案,自 Android 5.0 發佈以來,JobScheduler 已成爲執行後臺工作的首選方式,其工作方式有利於用戶。應用可以在安排作業的同時允許系統基於設備狀態、電源和連接情況等具體條件進行優化。JobScheduler 可實現控制和簡潔性,谷歌推出該機制是想要所有應用在執行後臺任務時使用它。(還有一點需要注意的是,在7.0上谷歌給出建議:在 Android 7.0 中,刪除了三個常用隱式廣播 —CONNECTIVITY_ACTION、ACTION_NEW_PICTURE 和ACTION_NEW_VIDEO— 因爲這些廣播可能會一次喚醒多個應用的後臺進程,同時會耗盡內存和電量。如果應用需要收到這些廣播,充分利用 Android 7.0 以遷移到 JobScheduler 和相關的 API。)

如何使用jobscheduler

構建屬於你的job任務

應用如果想使用JobScheduler API的話,首先需要創建自己需要執行的任務信息,創建任務的方法在谷歌官方文檔上已經有詳細介紹,這裏只是放出一個實例:


JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);  
 ComponentName jobService = new ComponentName(this, MyJobService.class);

 JobInfo jobInfo = new JobInfo.Builder(100012, jobService) //任務Id等於100012
         .setMinimumLatency(5000)// 任務最少延遲時間爲5s  
         .setOverrideDeadline(60000)// 任務deadline,當到期沒達到指定條件也會開始執行  
         .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)// 需要滿足網絡條件,默認值NETWORK_TYPE_NONE
         .setPeriodic(AlarmManager.INTERVAL_DAY) //循環執行,循環時長爲一天(最小爲15分鐘)
         .setRequiresCharging(true)// 需要滿足充電狀態  
         .setRequiresDeviceIdle(false)// 設備處於Idle(Doze)
         .setPersisted(true) //設備重啓後是否繼續執行
         .setBackoffCriteria(3000,JobInfo.BACKOFF_POLICY_LINEAR) //設置退避/重試策略
         .build();  
 scheduler.schedule(jobInfo);



上面的一個任務需要滿足充電狀態,並且設備不出於idle狀態,並且需要網絡處於非計費類型時,會運行該job,自己在構建自己的job 時候需要按需設置觸發條件。但是這裏需要注意的一個點是JobScheduler所創建並執行的任務必須是帶有條件限制的,不然是違背其初衷的,當你創建一個任務,並且不設置任何限制條件並且直接調用 scheduler.schedule(builder.build());去執行該任務是不可行的,會報以下的異常

    java.lang.IllegalArgumentException: You're trying to build a job with no constraints, this is not allowed.



1. setMinimumLatency(long minLatencyMillis): 設置任務的最小延遲執行時間(單位是毫秒)。
2. setOverrideDeadline(long maxExecutionDelayMillis): 設置任務最晚的延遲時間。如果到了規定的時間時其他條件還未滿足,你的任務也會被啓動。
3. setPersisted(boolean isPersisted): 設置當設備重啓之後該任務是否還要繼續執行。
4. setExtras(PersistableBundle extras): 設置傳遞bundler參數
5. setRequiredNetworkType(int networkType):設置需要滿足網絡類型
6. setRequiresCharging(boolean requiresCharging):設置是否需要充電狀態下運行
7. setRequiresDeviceIdle(boolean requiresDeviceIdle):設置是否需要在Idle(Doze)狀態下運行(7.0上新加的)
8. addTriggerContentUri(@NonNull TriggerContentUri uri):設置監控ContentUri 發生改變時運行(7.0上新加的)
9. setPeriodic(long intervalMillis):設置循環執行的時長。

等等接口都有官方解釋,需要設置什麼條件,使用對應設置即可。


Job任務要幹啥呢?

當需要使用JobScheduler 來幹實際的任務時,需要新建一個service,來繼承JobService(這一點與DreamService類似),而且必須重寫其中的兩個方法,分別是onStartJob(JobParameters params)和onStopJob(JobParameters params);
在startJob裏來執行自己的代碼邏輯

public class MyJobService extends JobService {
    @Override
    public boolean onStartJob(JobParameters params) {
        Log.i("MyTest", "onStartJob @@@@@@@@ " + params);
        jobFinished(params,true);
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
}



這裏需要注意的一點是,當做完自己的任務要及時調用JobFinished 來結束自己的任務。在6.0上在如果不調用該接口會造成嚴重的功耗問題。

當上面創建任務時執行到scheduler.schedule(builder.build()); 則開始準備執行任務,一旦滿足設置的條件,便會執行到onStartJob()方法,也就是在我們的任務應該具體事宜應該是放在onStartJob中去做的。該方法返回值爲爲一個boolean值,如果返回值是false,系統假設這個方法返回時任務已經執行完畢,如果返回值是true,那麼系統假定這個任務正要被執行,執行任務的重擔就落在了你的肩上(這時候需要去新開一個線程去做事物,文檔接口上有起描述)。當任務執行完畢後要調用jobFinished()來通知系統。

當系統受到一個cancel請求時會取消該任務(當該任務未執行將其在pending list刪除,如果該任務正在執行則停止其任務)。
使用Jobscheduler還需要到AndroidManifest.xml中添加一個service節點讓你的應用擁有綁定和使用這個JobService的權限。

<service android:name="com.example.apuser.jobtest.MyJobService"
    android:permission="android.permission.BIND_JOB_SERVICE" />



注意了,注意了,如果是在系統主線程中執行的scheduler.schedule(builder.build()); ,那麼你的MyJobService也是運行在你的主線程中的,此時需要考慮重點考慮ANR的問題了,如果執行網絡操作,或者文件IO 可能會直接被Android的StrickMode給檢測出來,拋異常出來 ,這時候就需要新起一個線程來做了。

號外,注意啦

本篇關於JobScheduler的分析開篇,主要介紹使用JobScheduler 的基本用法,以及對齊相關接口做了一些解釋。由於在工作中使用了Job 的場景較多,並且解決了幾例相關的bug ,對其中的一些需要注意的地方也有所總結:

1.小心JobService運行在主線程:上面已經說明了,當schedule是在主線程調用的,那麼jobsevice則運行則主線程,需要小心主線程ANR 以及嚴苛模式拋出異常
2.小心cancelAll(): 該方法的功能是取消該uid下的所有jobs,也就是說當存在多個app通過shareUid的方式,那麼在其中任意一個app執行cancalAll(),則會把所有同一uid下的app中的jobs都cancel掉。
3.Jobscheduler如果設置了setPersisted(true),則重啓後還會再運行,不能使用HashCode 來做JobId
4.job執行完了,一定要記得要調用JobFinished
5.同一個包名下,不能有兩個相同的jobId ,如果一個app非常大,兩個功能模塊爲兩個程序員維護,則很容易產生溝通不足的情況下,使用了兩個相同的JobId,此時只能有一個job生效。另一個無效。該問題可以通過終端命令行: adb shell dumpsys jobscheduler 來查看單個app 的job信息確認


總結

JobScheduler 雖然是在5.0上新增加的一個新服務,但是從L到M,N以及最新的O 上,谷歌Android也是在重點推薦使用該功能,並且在Android O 上谷歌還推出了一套Android vitals 計劃,旨在提高Android 系統的功耗,性能,以及穩定性等相關指標,在對功耗上提出來的建議便是,非精確性的定時任務建議使用Job來代替Alarm,能更加準確的滿足條件的執行你想要執行的任務。在Android O上JobScheduler更加完善了其條件控制,加上了低存儲,低電量策略下的job運行限制,這裏將在後面job服務解析中繼續提到

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章