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

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谈秫,隨后出現(xiàn)的幾起案子扒寄,更是在濱河造成了極大的恐慌,老刑警劉巖拟烫,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件该编,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡硕淑,警方通過(guò)查閱死者的電腦和手機(jī)上渴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)喜颁,“玉大人,你說(shuō)我怎么就攤上這事曹阔“肟” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵赃份,是天一觀(guān)的道長(zhǎng)寂拆。 經(jīng)常有香客問(wèn)我,道長(zhǎng)抓韩,這世上最難降的妖魔是什么纠永? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮谒拴,結(jié)果婚禮上尝江,老公的妹妹穿的比我還像新娘。我一直安慰自己英上,他們只是感情好炭序,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布啤覆。 她就那樣靜靜地躺著,像睡著了一般惭聂。 火紅的嫁衣襯著肌膚如雪窗声。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天辜纲,我揣著相機(jī)與錄音笨觅,去河邊找鬼。 笑死耕腾,一個(gè)胖子當(dāng)著我的面吹牛见剩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播幽邓,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼炮温,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了牵舵?” 一聲冷哼從身側(cè)響起柒啤,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎畸颅,沒(méi)想到半個(gè)月后担巩,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡没炒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年涛癌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片送火。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拳话,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出种吸,到底是詐尸還是另有隱情弃衍,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布坚俗,位于F島的核電站镜盯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏猖败。R本人自食惡果不足惜速缆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恩闻。 院中可真熱鬧艺糜,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至辱挥,卻和暖如春犁嗅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晤碘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工褂微, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人园爷。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓宠蚂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親童社。 傳聞我的和親對(duì)象是個(gè)殘疾皇子求厕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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