Android新架構(gòu)組件WorkManager

? ? ? ?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)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,055評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,346評(píng)論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,889評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,118評(píng)論 0 286
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,637評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,558評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,739評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,980評(píng)論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評(píng)論 1 280
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,347評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,702評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容