? ? ? ?WorkManager是谷歌2018年新推出的Architecture Components庫,主要用來管理后臺(tái)任務(wù)的執(zhí)行,保證任務(wù)在滿足執(zhí)行條件時(shí)即使應(yīng)用沒有啟動(dòng)也能執(zhí)行。WorkManager發(fā)布之前,有多種方式可以用來做后臺(tái)任務(wù),但它們都有各自的局限性;AsyncTask、ThreadPool、RxJava能夠在應(yīng)用中開后臺(tái)線程執(zhí)行任務(wù),但應(yīng)用關(guān)閉以后任務(wù)就無法執(zhí)行了;JobScheduler、Firebase的JobDispatcher則對(duì)android api level有要求;AlarmManager雖然支持所有的android版本,但針對(duì)不同api level也要調(diào)不同的接口來適應(yīng)行為變更。WorkManager會(huì)根據(jù)設(shè)備的 api level和app的運(yùn)行狀況選擇合適的方法來執(zhí)行后臺(tái)任務(wù)。當(dāng)app正在運(yùn)行時(shí)直接開辟新的線程來執(zhí)行;當(dāng)app沒有運(yùn)行時(shí),如果api level>=23則使用JobScheduler;api level >=14時(shí),如果應(yīng)用引用了Firebase則使用Firebase JobDispatcher;剩余情況則使用AlarmManager。
1.導(dǎo)入WorkManager
????在app/build.gradle文件中加入如下依賴配置:
????????kotlin:implementation"android.arch.work:work-runtime-ktx::$work_version
?????????java: implementation"android.arch.work:work-runtime::$work_version"
2.類和概念
????WorkManager API使用了一些不同的類,使用時(shí)需要繼承其中某個(gè)類。最主要的類包括以下幾個(gè):
????Worker:定義了需要執(zhí)行的任務(wù),該類是一個(gè)抽象類,需要我們繼承這個(gè)類并重寫它的doWork方法。該類會(huì)在運(yùn)行時(shí)被WorkManager實(shí)例化,然后在后臺(tái)線程執(zhí)行doWork方法。
????WorkRequest:代表類一個(gè)單獨(dú)的任務(wù),至少包含執(zhí)行任務(wù)的Worker類信息,此外你還可以向WorkRequest添加其他信息比如任務(wù)執(zhí)行條件。每個(gè)WorkRequest都有一個(gè)自動(dòng)生成的唯一Id,我們可以使用這個(gè)id來取消任務(wù)或者獲取任務(wù)的狀態(tài)。WorkRequest也是一個(gè)抽象類,我們需要使用它的直接子類,WorkManager目前支持兩個(gè)類型OneTimeWorkRequest和PeriodicWorkRequest創(chuàng)建WorkRequest需要使用WorkRequest.Builder類,我們實(shí)際用到的是它的子類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 來保存WorkStatus,通過觀察該LiveData我們可以獲取任務(wù)的當(dāng)前狀態(tài),并在任務(wù)執(zhí)行完畢后獲取返回值。
3.工作流程
3.1創(chuàng)建worker
創(chuàng)建一個(gè)Worker的子類,重寫doWork方法,在重寫doWork方法中執(zhí)行相關(guān)任務(wù),該方法返回值是一個(gè)Worker.Result對(duì)象,Result.SUCCESS表示任務(wù)執(zhí)行成功,Result.FAILURE表示任務(wù)執(zhí)行失敗,Result.RETRY表示任務(wù)暫時(shí)失敗,需要重新執(zhí)行(執(zhí)行條件通過 WorkRequest.Builder.setBackoffCriteria()方法設(shè)置);該類只指明如何執(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í)行,如果沒有提供任何約束條件,那么該任務(wù)將立即運(yùn)行。約束條件通過Constraints.Builder類創(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可通過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唯一工作序列
????通過調(diào)用?beginUniqueWork()?而不是beginWith()來創(chuàng)建唯一工作序列,每個(gè)唯一工作序列都有一個(gè)名稱,WorkManager同時(shí)只能執(zhí)行一個(gè)同名唯一工作序列,當(dāng)我們創(chuàng)建一個(gè)唯一工作序列時(shí),如果已經(jīng)有一個(gè)未完成的序列具有相同的名稱,則通過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
? ? 通過WorkRequest.Builder.addTag()方法將字符串tag傳遞給WorkRequest對(duì)象可以將任務(wù)進(jìn)行邏輯分組,WorkManager就可以通過特定方法對(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ù)可以通過WorkRequest.Builder.setInputData()方法設(shè)置,并通過Worker.getInputData()方法讀取。返回值通過?Worker.setOutputData()方法設(shè)置,并通過觀察?LiveData<WorkStatus>來獲取。
? ? 在鏈?zhǔn)饺蝿?wù)中,一個(gè)任務(wù)的輸出可用作下一個(gè)任務(wù)的輸入。如果是單個(gè)OneTimeWorkRequest跟在另一個(gè)OneTimeWorkRequest后面的簡(jiǎn)單鏈,第一個(gè)任務(wù)可通過setOutputData()設(shè)置結(jié)果,下一個(gè)任務(wù)可通過getInputData()來獲取結(jié)果。如果是復(fù)雜的鏈,例如多個(gè)任務(wù)同時(shí)發(fā)送結(jié)果到后一個(gè)任務(wù),可通過WorkRequest.Builder類的setInputMerger()方法指定一個(gè)InputMerger,通過該InputMerger來決定當(dāng)多個(gè)任務(wù)的輸出具有相同的key時(shí)該怎么做。InputMerger 是一個(gè)抽象類,我們需要使用它的子類:ArrayCreatingInputMerger,?OverwritingInputMerger。方法如下:
val builder = OneTimeWorkRequest.Builder(OneTimeWork1::class.java)
builder.setInputMerger(ArrayCreatingInputMerger::class.java)