? ? ? ?WorkManager是谷歌2018年新推出的Architecture Components庫(kù),主要用來(lái)管理后臺(tái)任務(wù)的執(zhí)行,保證任務(wù)在滿(mǎn)足執(zhí)行條件時(shí)即使應(yīng)用沒(méi)有啟動(dòng)也能執(zhí)行绪氛。WorkManager發(fā)布之前,有多種方式可以用來(lái)做后臺(tái)任務(wù)犯建,但它們都有各自的局限性在孝;AsyncTask硕糊、ThreadPool嬉荆、RxJava能夠在應(yīng)用中開(kāi)后臺(tái)線(xiàn)程執(zhí)行任務(wù),但應(yīng)用關(guān)閉以后任務(wù)就無(wú)法執(zhí)行了响蓉;JobScheduler硕勿、Firebase的JobDispatcher則對(duì)android api level有要求;AlarmManager雖然支持所有的android版本枫甲,但針對(duì)不同api level也要調(diào)不同的接口來(lái)適應(yīng)行為變更源武。WorkManager會(huì)根據(jù)設(shè)備的 api level和app的運(yùn)行狀況選擇合適的方法來(lái)執(zhí)行后臺(tái)任務(wù)。當(dāng)app正在運(yùn)行時(shí)直接開(kāi)辟新的線(xiàn)程來(lái)執(zhí)行;當(dāng)app沒(méi)有運(yùn)行時(shí),如果api level>=23則使用JobScheduler;api level >=14時(shí)想幻,如果應(yīng)用引用了Firebase則使用Firebase JobDispatcher粱栖;剩余情況則使用AlarmManager。
1.導(dǎo)入WorkManager
????在app/build.gradle文件中加入如下依賴(lài)配置:
????????kotlin:implementation"android.arch.work:work-runtime-ktx::$work_version
?????????java: implementation"android.arch.work:work-runtime::$work_version"
2.類(lèi)和概念
????WorkManager API使用了一些不同的類(lèi)脏毯,使用時(shí)需要繼承其中某個(gè)類(lèi)闹究。最主要的類(lèi)包括以下幾個(gè):
????Worker:定義了需要執(zhí)行的任務(wù),該類(lèi)是一個(gè)抽象類(lèi)抄沮,需要我們繼承這個(gè)類(lèi)并重寫(xiě)它的doWork方法跋核。該類(lèi)會(huì)在運(yùn)行時(shí)被WorkManager實(shí)例化,然后在后臺(tái)線(xiàn)程執(zhí)行doWork方法叛买。
????WorkRequest:代表類(lèi)一個(gè)單獨(dú)的任務(wù),至少包含執(zhí)行任務(wù)的Worker類(lèi)信息蹋订,此外你還可以向WorkRequest添加其他信息比如任務(wù)執(zhí)行條件率挣。每個(gè)WorkRequest都有一個(gè)自動(dòng)生成的唯一Id,我們可以使用這個(gè)id來(lái)取消任務(wù)或者獲取任務(wù)的狀態(tài)。WorkRequest也是一個(gè)抽象類(lèi)露戒,我們需要使用它的直接子類(lèi)椒功,WorkManager目前支持兩個(gè)類(lèi)型OneTimeWorkRequest和PeriodicWorkRequest創(chuàng)建WorkRequest需要使用WorkRequest.Builder類(lèi),我們實(shí)際用到的是它的子類(lèi)OneTimeWorkRequest.Builder和PeriodicWorkRequest.Builder智什。
????WorkManager:將WorkRequest加入隊(duì)列并管理动漾,我們將WorkRequest傳遞給WorkManager,WorkManager將WorkManager加入隊(duì)列并保證其在指定條件下執(zhí)行荠锭,從而達(dá)到分散系統(tǒng)資源負(fù)載的目的旱眯。
????WorkStatus:包含了相關(guān)任務(wù)的信息,WorkManager為每一個(gè)WorkRequest提供了LiveData 來(lái)保存WorkStatus证九,通過(guò)觀(guān)察該LiveData我們可以獲取任務(wù)的當(dāng)前狀態(tài)删豺,并在任務(wù)執(zhí)行完畢后獲取返回值。
3.工作流程
3.1創(chuàng)建worker
創(chuàng)建一個(gè)Worker的子類(lèi)愧怜,重寫(xiě)doWork方法呀页,在重寫(xiě)doWork方法中執(zhí)行相關(guān)任務(wù),該方法返回值是一個(gè)Worker.Result對(duì)象拥坛,Result.SUCCESS表示任務(wù)執(zhí)行成功蓬蝶,Result.FAILURE表示任務(wù)執(zhí)行失敗尘分,Result.RETRY表示任務(wù)暫時(shí)失敗,需要重新執(zhí)行(執(zhí)行條件通過(guò) WorkRequest.Builder.setBackoffCriteria()方法設(shè)置)丸氛;該類(lèi)只指明如何執(zhí)行任務(wù)培愁, 并不包含該任務(wù)何時(shí)執(zhí)行的任何信息。
3.2WorkRequest
運(yùn)行一次性任務(wù)需要?jiǎng)?chuàng)建OneTimeWorkRequest對(duì)象雪位, 運(yùn)行周期任務(wù)則創(chuàng)建PeriodicWorkRequest對(duì)象竭钝。如下
private fun createRequest(tag:String?):OneTimeWorkRequest{
????val builder = OneTimeWorkRequest.Builder(OneTimeWork::class.java)
????builder.setInputData(createData())
????builder.setConstraints(createConstraints())
????return builder.build()
}
private fun createRequest(tag:String?):PeriodicWorkRequest{
????????val builder = PeriodicWorkRequest.Builder(PeriodicWork::class.java, 5, TimeUnit.HOURS)
????????builder.setInputData(createData())
????????builder.setConstraints(createConstraints())
????????return builder.build()
}
3.3任務(wù)約束條件
????我們可以定義約束條件以告訴WorkManager何時(shí)安排任務(wù)執(zhí)行,如果沒(méi)有提供任何約束條件雹洗,那么該任務(wù)將立即運(yùn)行香罐。約束條件通過(guò)Constraints.Builder類(lèi)創(chuàng)建。
private fun createConstraints(): Constraints {
????val builder = Constraints.Builder()
????builder.setRequiredNetworkType(NetworkType.NOT_REQUIRED)
????builder.setRequiresBatteryNotLow(false)
????.setRequiresCharging(false)
????.setRequiresStorageNotLow(false)
????if(Build.VERSION.SDK_INT >=23)
????????builder.setRequiresDeviceIdle(false)
????if(Build.VERSION.SDK_INT >=24)
????????builder.addContentUriTrigger(ContactsContract.Contacts.CONTENT_URI, true)
????return builder.build()
}
3.4執(zhí)行請(qǐng)求
????WorkManager.getInstance().enqueue(request)
3.5取消任務(wù)
????WorkManager.getInstance().cancelWorkById(worker.getId())
4.高級(jí)應(yīng)用
4.1鏈?zhǔn)饺蝿?wù)
WorkManager可通過(guò)beginWith()方法生成一個(gè)WorkContinuation 對(duì)象时肿,該對(duì)象允許我們將不同的WorkRequest鏈接在一起按照特定順序執(zhí)行庇茫。例如定義五個(gè)workrequest:request1、request2螃成、request3旦签、request4、request5寸宏;
WorkManager.getInstance()?.apply {
? ? beginWith(request1)
????.then(request2)
????.then(request3)
.????then(request4)
????.then(request5)
????.enqueue()
}
在這種方式下的執(zhí)行順序?yàn)閞equest1 ->?request2 ->?request3 ->?request4 ->?request5宁炫。
WorkManager.getInstance()?.apply {
? ? beginWith(request1, request2)
????.then(request3, request4)
????.then(request5)
????.enqueue()
}
這種方式下執(zhí)行順序?yàn)閞equest1、 request2 ->?request3 氮凝、 request4 ->?request5羔巢,其中request1、 request2的執(zhí)行順序不一定罩阵;?request3 竿秆、 request4亦然。
WorkManager.getInstance()?.apply {
? ? val continuation1 = beginWith(request1, request2)
????val continuation2 = beginWith(request3, request4, request5)
????WorkContinuation.combine(continuation1, continuation2).enqueue()
}
這種情況下continuation1和continuation2執(zhí)行順序不一定稿壁,其各自包含的request如果有執(zhí)行順序則按給定的執(zhí)行順序執(zhí)行幽钢。
WorkManager.getInstance()?.apply {
? ? val continuation = beginWith(request2, request3, request4, request5)
????WorkContinuation.combine(request1, continuation).enqueue()
}
這種情況下當(dāng)continuation中序列執(zhí)行完成后執(zhí)行request1
4.2唯一工作序列
????通過(guò)調(diào)用?beginUniqueWork()?而不是beginWith()來(lái)創(chuàng)建唯一工作序列,每個(gè)唯一工作序列都有一個(gè)名稱(chēng)傅是,WorkManager同時(shí)只能執(zhí)行一個(gè)同名唯一工作序列匪燕,當(dāng)我們創(chuàng)建一個(gè)唯一工作序列時(shí),如果已經(jīng)有一個(gè)未完成的序列具有相同的名稱(chēng)落午,則通過(guò)ExistingWorkPolicy指定WorkManager應(yīng)執(zhí)行的操作:
ExistingWorkPolicy.KEEP:保持現(xiàn)有工作序列并忽略新的請(qǐng)求
ExistingWorkPolicy.REPLACE:取消現(xiàn)有的序列并用新序列替換
ExistingWorkPolicy.APPEND:將新序列附加到現(xiàn)有序列谎懦,在現(xiàn)有序列的最后一個(gè)任務(wù)完成后運(yùn)行新序列的第一個(gè)任務(wù)
WorkManager.getInstance()?.apply {
? ? beginUniqueWork("a", ExistingWorkPolicy.KEEP, request1).enqueue()
????beginUniqueWork("a", ExistingWorkPolicy.KEEP, request2).enqueue()
}
4.3 tag
? ? 通過(guò)WorkRequest.Builder.addTag()方法將字符串tag傳遞給WorkRequest對(duì)象可以將任務(wù)進(jìn)行邏輯分組,WorkManager就可以通過(guò)特定方法對(duì)具有相同tag的任務(wù)進(jìn)行操作溃斋。例如:WorkManager.cancelAllWorkByTag()界拦,WorkManager.getStatusesByTag()等
4.4輸入?yún)?shù)和返回值
????輸入?yún)?shù)和返回值是一個(gè)Data對(duì)象,該對(duì)象里面保存有一個(gè)HashMap,所有參數(shù)都以鍵值對(duì)的形式保存在里面梗劫。輸入?yún)?shù)可以通過(guò)WorkRequest.Builder.setInputData()方法設(shè)置享甸,并通過(guò)Worker.getInputData()方法讀取截碴。返回值通過(guò)?Worker.setOutputData()方法設(shè)置,并通過(guò)觀(guān)察?LiveData<WorkStatus>來(lái)獲取蛉威。
? ? 在鏈?zhǔn)饺蝿?wù)中日丹,一個(gè)任務(wù)的輸出可用作下一個(gè)任務(wù)的輸入。如果是單個(gè)OneTimeWorkRequest跟在另一個(gè)OneTimeWorkRequest后面的簡(jiǎn)單鏈蚯嫌,第一個(gè)任務(wù)可通過(guò)setOutputData()設(shè)置結(jié)果哲虾,下一個(gè)任務(wù)可通過(guò)getInputData()來(lái)獲取結(jié)果。如果是復(fù)雜的鏈择示,例如多個(gè)任務(wù)同時(shí)發(fā)送結(jié)果到后一個(gè)任務(wù),可通過(guò)WorkRequest.Builder類(lèi)的setInputMerger()方法指定一個(gè)InputMerger束凑,通過(guò)該InputMerger來(lái)決定當(dāng)多個(gè)任務(wù)的輸出具有相同的key時(shí)該怎么做。InputMerger 是一個(gè)抽象類(lèi)栅盲,我們需要使用它的子類(lèi):ArrayCreatingInputMerger,?OverwritingInputMerger汪诉。方法如下:
val builder = OneTimeWorkRequest.Builder(OneTimeWork1::class.java)
builder.setInputMerger(ArrayCreatingInputMerger::class.java)