- 背景:通常我們在開發(fā)過程中處理后臺任務(wù)的時候可能是自己維系一個線程池或者通過一個后臺任務(wù)來完成我們的工作枪孩,然后對于某些特定的需求還需要自己進行網(wǎng)絡(luò)條件奏属,電量等信息的攔截判斷處理福压。這個實現(xiàn)過程比較繁瑣仿村,而且每個類似的需求都寫一套相同的邏輯就顯得很累贅了锐朴。通過谷歌新推出的WorkManager SDK能很好的解決上述問題
- 原理:根據(jù)當前系統(tǒng)的API版本和當前應(yīng)用的存活狀態(tài)來選擇對應(yīng)的方式來執(zhí)行任務(wù),當應(yīng)用在運行的時候蔼囊,會在應(yīng)用進程中開啟一個子線程來執(zhí)行任務(wù)包颁,如果應(yīng)用被殺掉了,WorkManager會根據(jù)當前系統(tǒng)API版本來選擇是使用JobScheduler(API21)還是AlarmManager(API14-API20)來執(zhí)行任務(wù)(具體的執(zhí)行時機在某些國產(chǎn)系統(tǒng)上可能會發(fā)生變化)压真。
- 結(jié)論:對于可延時的執(zhí)行的后臺任務(wù)建議遷移到WorkManager上娩嚼,節(jié)約APP的電量消耗(如日志上報等),而對于需要立刻執(zhí)行的任務(wù)建議還是使用ThreadPool或者前臺服務(wù)等
一滴肿、基本功能
-
1.1 運作流程
- 通過WorkManager將Woker任務(wù)添加到任務(wù)隊列中岳悟,在添加的過程中可以指定約束條件(充電狀態(tài)、WIFI狀態(tài)泼差、手機存儲空間等等)贵少。只有當約束條件滿足時才會執(zhí)行我們的Worker。
-
1.2 Worker
- 表示一個要執(zhí)行的任務(wù)堆缘,我們需要繼承并顯示該類滔灶,并在doWork方法中實現(xiàn)我們自己的任務(wù)邏輯,通過不同的返回值表示當前Work的執(zhí)行情況
- Worker.Result.SUCCES 表示任務(wù)執(zhí)行成功吼肥,如果有下一個任務(wù)會接著執(zhí)行下一個任務(wù)
- Worker.Result.FAILURE 表示任務(wù)執(zhí)行失敗录平,整個任務(wù)鏈就此中斷
- Worker.Result.RETRY 通過WorkManager根據(jù)重試策略嘗試執(zhí)行該任務(wù)
- 表示一個要執(zhí)行的任務(wù)堆缘,我們需要繼承并顯示該類滔灶,并在doWork方法中實現(xiàn)我們自己的任務(wù)邏輯,通過不同的返回值表示當前Work的執(zhí)行情況
-
1.3 WorkRequest
- Worker的包裝類麻车,最后添加到WorkManager中的就是當前WorkRequest對象,通過WorkRequest對象可以給我們的Worker附加額外的功能
- 執(zhí)行任務(wù)時的約束條件(電量斗这、網(wǎng)絡(luò)等)
- 當Worker執(zhí)行失敗時的重試策略
- 設(shè)置當前Worker的入?yún)ⅲ▓?zhí)行任務(wù)時可攜帶額外的參數(shù)給Worker使用)
- 設(shè)置當前Worker接受的入?yún)⒑喜⒉呗裕▋H當上一個任務(wù)存在多個并行的Worker且返回相同的KEY時有作用动猬,在下面的入?yún)⒑统鰠⒛K會詳細介紹)
- 兩個子類
- OneTimeWorkRequest
- PeriodicWorkRequest(下方模塊有介紹)
- Worker的包裝類麻车,最后添加到WorkManager中的就是當前WorkRequest對象,通過WorkRequest對象可以給我們的Worker附加額外的功能
1.4 WorkManager
* 主要用來管理任務(wù)請求和任務(wù)隊列,將Worker的包裝者WorkRequest對象入隊表箭,同時還可以指定多個任務(wù)執(zhí)行的先后順序赁咙,實現(xiàn)串行或者并行的任務(wù)鏈。-
1.5 Constraints(添加約束條件)
- setRequiredNetworkType(設(shè)置網(wǎng)絡(luò)相關(guān)的約束條件)
- NetworkType.NOT_REQUIRED【對網(wǎng)絡(luò)沒有要求】
- NetworkType.CONNECTED【網(wǎng)絡(luò)連接】
- NetworkType.UNMETERED【不計費的網(wǎng)絡(luò)比如WIFI下執(zhí)行】
- NetworkType.NOT_ROAMING【非漫游網(wǎng)絡(luò)狀態(tài)】
- NetworkType.METERED 【計費網(wǎng)絡(luò)比如3G免钻,4G下執(zhí)行彼水。】
- setRequiresDeviceIdle (設(shè)置設(shè)備是否空閑)
- setRequiresCharging(設(shè)置設(shè)備是否處于充電狀態(tài))
- setRequiresBatteryNotLow(設(shè)備電量是否不應(yīng)低于臨界閥值)
- setRequiresStorageNotLow(設(shè)備存儲空間低于臨界閥值)
- addTriggerContentUri【API24】(對某個ContentUri的內(nèi)容變化進行監(jiān)聽)
- 通過內(nèi)容提供者修改聯(lián)系人极舔,當聯(lián)系人修改時猿涨,觸發(fā)這個監(jiān)聽回調(diào)
注:因為內(nèi)容提供者的修改可能會短時間內(nèi)多次調(diào)用,為了不影響監(jiān)聽線程姆怪,這里需要另起新的線程或者來執(zhí)行
- 通過內(nèi)容提供者修改聯(lián)系人极舔,當聯(lián)系人修改時猿涨,觸發(fā)這個監(jiān)聽回調(diào)
- setRequiredNetworkType(設(shè)置網(wǎng)絡(luò)相關(guān)的約束條件)
二叛赚、高級功能
- 2.1 任務(wù)類型
- OneTimeWorkRequest 【只執(zhí)行一次的任務(wù)】
OneTimeWorkRequest myWorkRequest = new OneTimeWorkRequest.Builder(MyWorker.class).build();
- PeriodicWorkRequest 【一定周期間隔內(nèi)重復(fù)執(zhí)行的任務(wù)】
- 周期性任務(wù),官方設(shè)定最短執(zhí)行周期為15分鐘稽揭,同時因為系統(tǒng)的優(yōu)化策略俺附,后面周期性的任務(wù)執(zhí)行可能會有一定的時間延遲,無法做到精準時間執(zhí)行溪掀。
PeriodicWorkRequest build = new PeriodicWorkRequest.Builder(MyWorker.class, 25, TimeUnit.MILLISECONDS).build();
- OneTimeWorkRequest 【只執(zhí)行一次的任務(wù)】
- 2.2 任務(wù)鏈
- 串行鏈
WorkManager.getInstance().beginWith(workRequestA) .then(workRequestB) .then(workRequestC) .enqueue()
- 依次執(zhí)行workRequestA 事镣,workRequestB,workRequestC揪胃。每個階段完成之后通過在doWork方法中返回Worker.Result.SUCCES來進入下一個workRequest璃哟。如果其中任何一個workRequest返回Worker.Result.FAILURE,那么后續(xù)的workRequest都不會執(zhí)行了喊递,就此中斷
- 并發(fā)鏈
WorkManager.getInstance() .beginWith(workA1, workA2, workA3) .then(workB1, workB2) .then(workC1, workC2) .enqueue();
- 首先并行執(zhí)行workA1, workA2, workA3任務(wù)随闪,等待這三個任務(wù)都執(zhí)行完之后再并發(fā)執(zhí)行workB1, workB2任務(wù),等待著兩個任務(wù)執(zhí)行完畢之后再繼續(xù)并發(fā)執(zhí)行workC1, workC2任務(wù)骚勘。
- 組合鏈
//A,B任務(wù)鏈 WorkContinuation continuationAB = WorkManager.getInstance().beginWith(requestA).then(requestB); //C,D任務(wù)鏈 WorkContinuation continuationCD = WorkManager.getInstance().beginWith(requestC).then(requestD); // 對AB和CD進行組合 WorkContinuation.combine(continuationAB, continuationCD).then(requestE).enqueue();
- A/B串行執(zhí)行铐伴,C/D串行執(zhí)行,最后讓A/B和C/D這兩個組合任務(wù)并發(fā)執(zhí)行俏讹。
- 串行鏈
- 2.3 任務(wù)標簽
- 作用:對WorkerRequest進行標簽分組当宴,同時還可以操作同一分組標簽,比如取消該標簽下的所有任務(wù)泽疆,或者獲取該標簽下的所有任務(wù)户矢,然后查詢各個任務(wù)現(xiàn)在的執(zhí)行狀態(tài)
OneTimeWorkRequest cacheCleanupTask =new OneTimeWorkRequest.Builder(WorkerA.class) .addTag("groupA") .build();
- 2.4 任務(wù)參數(shù)傳遞
- 作用:當我們在執(zhí)行單個任務(wù)或者一個任務(wù)鏈的時候,我們可能需要將參數(shù)帶入到第一個任務(wù)中殉疼,或者需要將第一個任務(wù)執(zhí)行后的返回結(jié)果帶入到第二個任務(wù)中梯浪。
- 初始化任務(wù)時傳遞參數(shù):
Data workData = new Data.Builder() // Data大小需要控制在10k內(nèi) .putInt("NAME", "Android Develop") // 參數(shù)可以為任意類型 .putInt("AGE", 101) .build(); OneTimeWorkRequest workRequestA = new OneTimeWorkRequest.Builder(WorkerA.class) .setInputData(workData) .build(); WorkManager.getInstance().enqueue(mathWork);
- 將當前任務(wù)的執(zhí)行結(jié)果傳遞給下一個任務(wù)
public class MathWorker extends Worker { @Override public Worker.WorkerResult doWork() { String sourceName = getInputData().getString("NAME", ""); int sourceAge = getInputData().getInt("AGE", 0); int formatAge = sourceAge + 101; Data output = new Data.Builder() .putInt("AGE", formatAge) .build(); setOutputData(output); return WorkerResult.SUCCESS; }}
- 多個相同參數(shù)來源的合并
- 參數(shù)合并:當需要將多個任務(wù)的輸出作為一個任務(wù)的輸入時就會涉及到一個參數(shù)合并的問題捌年,如果多個任務(wù)的輸出中由好幾個key都是一樣的,那么需要由開發(fā)者自己來決定應(yīng)該使用什么樣的參數(shù)合并規(guī)則
- 合并規(guī)則之ArrayCreatingInputMerger
OneTimeWorkRequest workerRequestA = new OneTimeWorkRequest.Builder(WorkerA.class).setInputMerger(ArrayCreatingInputMerger.class).build();
- 對于該合并規(guī)則來說驱证,同一個的Key對應(yīng)的Value都會放在一個數(shù)組中延窜,數(shù)據(jù)會隨著入?yún)⒌脑龆喽兇罅低螅瑢τ谙掠稳蝿?wù)想要獲取數(shù)據(jù)的方法為getXXXArray()抹锄,如getIntArray();
- 合并規(guī)則之OverwritingInputMerger
OneTimeWorkRequest workerRequestB = new OneTimeWorkRequest.Builder(WorkerA.class).setInputMerger(OverwritingInputMerger.class).build();
- 顧名思義,后到的key-value會覆蓋已存在的key-value
- 自定義合并規(guī)則
- 需要自定義類荠藤,然后繼承抽象類InputMerger伙单,覆寫merge方法,實現(xiàn)自己的merge邏輯
三哈肖、存在的限制
- 周期任務(wù)最小時間間隔為15分鐘
- 任務(wù)隊列最大限制為100個
- 參數(shù)傳遞最大數(shù)據(jù)量為10kb