社区说|浅谈 WorkManager 的设计与实现:系统概述
什么是 社區(qū)說(shuō) ?
反思 系列博客是一種看似 “內(nèi)卷” ,但卻 效果顯著 的學(xué)習(xí)方式,該系列起源和目錄請(qǐng)參考 這里 。
困境
作為一名 Android 開(kāi)發(fā)者,即使你沒(méi)有用過(guò),也一定對(duì) WorkManager 耳熟能詳。
自2018年發(fā)布以來(lái),作為 Google 官方推出的架構(gòu)組件,它未像 LiveData、ViewModel 一樣廣泛應(yīng)用。究其原因,一起來(lái)看 官方 當(dāng)初對(duì) WorkManager 的描述:
WorkManager 用于執(zhí)行可 延遲、異步 的后臺(tái)任務(wù)。它提供了一個(gè) 可靠、可調(diào)度 的后臺(tái)任務(wù)執(zhí)行環(huán)境,可以處理 即使在應(yīng)用退出或設(shè)備重啟后仍需要運(yùn)行 的任務(wù)。
快速提煉重點(diǎn),我們得到了什么?
WorkManager,可以處理后臺(tái)任務(wù),這些任務(wù)哪怕手機(jī)重啟也一定會(huì)執(zhí)行?
看完簡(jiǎn)介,我的內(nèi)心毫無(wú)波瀾,“關(guān)機(jī)重啟仍會(huì)執(zhí)行” 的確很不錯(cuò),but who cares ? 我根本用不到這些。
它給人的第一印象并不驚艷,甚至可以說(shuō) 平平無(wú)奇 ,無(wú)論是相親市場(chǎng)還是技術(shù)領(lǐng)域,這都非常致命。
經(jīng)過(guò)一系列的實(shí)踐和研究后,回過(guò)頭再看 WorkManager,筆者認(rèn)為 WorkManager 是傳統(tǒng) Android 領(lǐng)域內(nèi)學(xué)院派編程風(fēng)格的代表作,是滄海遺珠。它為 后臺(tái)任務(wù)的處理和調(diào)度 提供了一個(gè)優(yōu)秀的解決方案。
即使如此,WorkManager 仍和 Paging 面臨著同樣的 困境: 難以推廣、默默無(wú)聞。簡(jiǎn)單的項(xiàng)目用不到,復(fù)雜的項(xiàng)目經(jīng)過(guò)若干年的沉淀,該領(lǐng)域早已應(yīng)用了其它方案,學(xué)習(xí)和遷移成本過(guò)高,以至不被需要。
——時(shí)至今日,社區(qū)內(nèi)除了若干 使用簡(jiǎn)介 和 源碼分析 的博客單篇,我們?nèi)院茈y找到其 實(shí)戰(zhàn)進(jìn)階 或 最佳實(shí)踐 的相關(guān)系列。
目的
本文筆者將通過(guò)針對(duì) Android 的后臺(tái)任務(wù)管理機(jī)制,進(jìn)行一個(gè)系統(tǒng)性的分析和設(shè)計(jì)。
最終的目的,并非是讓讀者將 WorkManager 強(qiáng)行引入和使用,而是對(duì) 后臺(tái)任務(wù)的管理和調(diào)度工具 (后文簡(jiǎn)稱 后臺(tái)任務(wù)庫(kù) )有一個(gè)清晰的認(rèn)知——即使從未使用過(guò),在將來(lái)的某一天,遇到類似的業(yè)務(wù)訴求時(shí),也能快速形成一個(gè)清晰的思路和方案。
基本概念
想要構(gòu)建一個(gè)優(yōu)秀的后臺(tái)任務(wù)庫(kù),需要依靠不斷的迭代、優(yōu)化和擴(kuò)展,最終成為一個(gè)靈活、完善的工具。
那么 后臺(tái)任務(wù)庫(kù) 需要提供哪些功能,為什么要設(shè)計(jì)這些功能?
第一個(gè)映入眼簾的概念是:線程調(diào)度,顧名思義,它是后臺(tái)異步任務(wù)的基石。
1.線程調(diào)度
舉個(gè)例子,你負(fù)責(zé)的是一個(gè)視頻類APP的研發(fā),最初的業(yè)務(wù)訴求如下:
APP 的日志上報(bào)。
需求清晰明了,顯然,日志上報(bào)是一個(gè) 后臺(tái)異步任務(wù),在子線程進(jìn)行 IO 操作: Log 文件本地的讀寫(xiě),以及上傳到服務(wù)器。
這里我們引入 任務(wù)執(zhí)行器(TaskExecutor)的概念:其用于執(zhí)行后臺(tái)任務(wù)的具體邏輯。通常,我們使用 線程池 來(lái)管理任務(wù)的線程,并提供線程的 創(chuàng)建、銷毀、調(diào)度 等功能:
// androidx.work.impl.utils.taskexecutor.WorkManagerTaskExecutor public class WorkManagerTaskExecutor implements TaskExecutor {final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());// 1.可切換主線程private final Executor mMainThreadExecutor = new Executor() {@Overridepublic void execute(@NonNull Runnable command) {mMainThreadHandler.post(command);}};// 2.可切換后臺(tái)工作線程private final Executor mBackgroundExecutor = Executors.newFixedThreadPool(Math.max(2, Math.min(Runtime.getRuntime().availableProcessors() - 1, 4)),createDefaultThreadFactory(isTaskExecutor)); }為提高可讀性,本文的示例代碼都有 大幅精簡(jiǎn) ,讀者應(yīng)盡量避免「只見(jiàn)樹(shù)木不見(jiàn)森林」,以理解設(shè)計(jì)理念為主。
這里我們?yōu)榻M件提供了最基礎(chǔ)的線程調(diào)度的能力,便于內(nèi)部實(shí)現(xiàn) 主線程 和 后臺(tái)線程 的切換。其中我們?yōu)楹笈_(tái)線程申請(qǐng)了一個(gè)線程池,并根據(jù)可用處理器核心數(shù)量來(lái)設(shè)置適當(dāng)?shù)木€程數(shù)(通常同時(shí)最多執(zhí)行的任務(wù)數(shù)為4),以充分利用設(shè)備的性能。
2.任務(wù)隊(duì)列和串行化
接下來(lái)我們?cè)O(shè)計(jì)一個(gè)任務(wù)隊(duì)列,保證可以不斷接收新的任務(wù),并及時(shí)分發(fā)給空閑的后臺(tái)線程執(zhí)行:
public class ArrayDeque<E> extends AbstractCollection<E> implements Deque<E> {public boolean add(E e);public boolean offer(E e);public E remove();public E poll(); }很經(jīng)典的一個(gè)隊(duì)列接口,WorkManager 也并未單獨(dú)造輪子,而是借用了 Java 的 ArrayDeque 類。這是一個(gè)非常經(jīng)典的實(shí)現(xiàn)類,在諸多大名鼎鼎的工具庫(kù)中都有它的身影(比如RxJava),限于篇幅不展開(kāi),感興趣的讀者可自行查看源碼。
讀者可預(yù)見(jiàn)到的是,后臺(tái)任務(wù)的創(chuàng)建和添加的時(shí)機(jī)是無(wú)法控制的,加上 ArrayDeque 設(shè)計(jì)之初都并未考慮線程同步,因此目前的設(shè)計(jì)將會(huì)有 線程安全問(wèn)題 。
因此,后臺(tái)任務(wù)的入列和執(zhí)行,必須借用一個(gè)新的角色保證串行,就像單線程一樣。
實(shí)現(xiàn)方案非常簡(jiǎn)單,通過(guò)代理和加鎖,提供一個(gè)TaskExecutor的包裝類即可:
public class SerialExecutorImpl implements SerialExecutor {// 1. 任務(wù)隊(duì)列private final ArrayDeque<Task> mTasks;// 2. 后臺(tái)線程的Executor,即上文中數(shù)量為4的線程池private final Executor mBackgroundExecutor;// 3.鎖對(duì)象@GuardedBy("mLock")private Runnable mActive;@Overridepublic void execute(@NonNull Runnable command) {// 不斷地入列、出列和任務(wù)執(zhí)行synchronized (mLock) {mTasks.add(new Task(this, command));if (mActive == null) {if ((mActive = mTasks.poll()) != null) {mExecutor.execute(mActive);}}}} }3.任務(wù)狀態(tài)、類型和結(jié)果回調(diào)
下一步,我們對(duì)所關(guān)注的任務(wù)狀態(tài)進(jìn)行一個(gè)羅列,不難得出,我們關(guān)注的狀態(tài)大致有:任務(wù)開(kāi)始(Enqueued)、任務(wù)執(zhí)行中(Running)、任務(wù)成功(Successded)、任務(wù)失敗(Failed)、任務(wù)取消(Cancelled)等幾種:
請(qǐng)注意,上文中的日志上報(bào)我們定義成了 一次性工作,即只執(zhí)行一次,執(zhí)行結(jié)束即永久結(jié)束。
實(shí)際上,一次性工作 并不能涵蓋所有的業(yè)務(wù)場(chǎng)景,舉例來(lái)說(shuō),作為一個(gè)視頻類的 APP,我們需 定期 對(duì)用戶的播放進(jìn)度進(jìn)行一次記錄或上報(bào),保證用戶即使殺掉APP,下次仍在最后記錄的播放位置繼續(xù)播放。
這里我們引入了 定期任務(wù) 的概念,它只有一個(gè)終止?fàn)顟B(tài) CANCELLED。這是因?yàn)槎ㄆ诠ぷ饔肋h(yuǎn)不會(huì)結(jié)束。每次運(yùn)行后,無(wú)論結(jié)果如何,系統(tǒng)都會(huì)重新對(duì)其進(jìn)行調(diào)度。
最后,我們將后臺(tái)任務(wù)的執(zhí)行抽象為一個(gè)接口,開(kāi)發(fā)者實(shí)現(xiàn) doWork 接口,實(shí)現(xiàn)具體后臺(tái)業(yè)務(wù),如日志上報(bào)等,并返回具體的結(jié)果:
public abstract class Worker {// 后臺(tái)任務(wù)的具體實(shí)現(xiàn),`WorkerManager`內(nèi)部進(jìn)行了線程調(diào)度,執(zhí)行在工作線程.@WorkerThreadpublic abstract @NonNull Result doWork(); }public abstract static class Result {@NonNullpublic static Result success() {return new Success();}@NonNullpublic static Result retry() {return new Retry();}@NonNullpublic static Result failure() {return new Failure();} }持久化
目前為止,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)化版、基于內(nèi)存中隊(duì)列的后臺(tái)任務(wù)庫(kù),接下來(lái)我們將針對(duì) 后臺(tái)任務(wù)持久化 的必要性,進(jìn)行進(jìn)一步的討論。
1.非即時(shí)任務(wù)
第一步我們需要引入 非即時(shí)任務(wù) 的概念。
作為互聯(lián)網(wǎng)的從業(yè)者,讀者對(duì)類似的提示彈窗應(yīng)該并不陌生:
您手機(jī)/PC的新版本已下載完畢:「立即安裝」、「定時(shí)安裝」、「稍后提醒我」
顯然,這是一個(gè)常見(jiàn)的功能,用戶選擇后,應(yīng)用或系統(tǒng)的后臺(tái)需在未來(lái)的某個(gè)時(shí)間點(diǎn),升級(jí)或提醒用戶。如果和前文中的任務(wù)類型進(jìn)行區(qū)分,前者我們可以歸納為 即時(shí)任務(wù),后者則可稱之為 延時(shí)任務(wù) 或 非即時(shí)任務(wù)。
非即時(shí)任務(wù)的訴求,將會(huì)使我們現(xiàn)有 后臺(tái)任務(wù)庫(kù) 的 復(fù)雜度呈指數(shù)級(jí)提升 ,但這是 必要 的,原因如下:
首先,上文中 定期任務(wù) 也屬于非即時(shí)任務(wù)的范疇,雖然該任務(wù)是立即執(zhí)行并等待的,但實(shí)際上其真正的業(yè)務(wù)邏輯,仍是未來(lái)的某個(gè)時(shí)間點(diǎn)觸發(fā);其次,也是最重要的一點(diǎn),作為一個(gè)健壯的后臺(tái)任務(wù)庫(kù),和 即時(shí)任務(wù) 相比,對(duì) 非即時(shí)任務(wù) 提供支持的優(yōu)先級(jí)要高得多。
——這似乎違反直覺(jué),在日常開(kāi)發(fā)中, 即時(shí)任務(wù) 似乎才是主流,但我們忽視了一個(gè)事實(shí),資源并非無(wú)限。
在文章的開(kāi)始,我們構(gòu)建了基本的線程調(diào)度能力,并創(chuàng)建了一個(gè)數(shù)量為 4 的線程池。但隨著業(yè)務(wù)復(fù)雜度的提升,線程池可能會(huì)同時(shí)執(zhí)行多個(gè)任務(wù),這意味著部分晚入列、或優(yōu)先級(jí)低的任務(wù),會(huì)經(jīng)常性等待前面的任務(wù)執(zhí)行完畢。
嚴(yán)格意義上講,此時(shí),即時(shí)任務(wù)都轉(zhuǎn)化為了非即時(shí)任務(wù),再進(jìn)一步抽象,所有即時(shí)任務(wù)都是非即時(shí)任務(wù)。
萬(wàn)物皆可異步,是異步編程的一個(gè)經(jīng)典概念,該思想在 Handler、RxJava 或 協(xié)程 中都有體現(xiàn)。
即時(shí)任務(wù)被延時(shí)執(zhí)行是合理的嗎?對(duì)于后臺(tái)任務(wù)而言,是非常合理的,如果開(kāi)發(fā)者有明確的訴求, 必須 且 立即 執(zhí)行某段業(yè)務(wù)邏輯,那么就不應(yīng)該用 后臺(tái)任務(wù)庫(kù),而是直接在內(nèi)存中調(diào)用這塊代碼。
2.持久化存儲(chǔ)
當(dāng)后臺(tái)任務(wù)可能被延時(shí)執(zhí)行,思考下一個(gè)問(wèn)題,如何保證任務(wù)執(zhí)行的可靠性?
終極解決方案必然是 后臺(tái)任務(wù)持久化,通過(guò)本地文件或者數(shù)據(jù)庫(kù)存儲(chǔ)后,即使進(jìn)程被用戶殺掉或系統(tǒng)回收,在合適的時(shí)機(jī),APP總是能夠?qū)⑷蝿?wù)恢復(fù)并重建。
考慮到安全性,WorkManager 最終選擇使用了 Room 數(shù)據(jù)庫(kù),并且設(shè)計(jì)維護(hù)了一個(gè)非常復(fù)雜的 Database,簡(jiǎn)單羅列下核心的 WorkSpec 表:
@Entity(indices = [Index(value = ["schedule_requested_at"]), Index(value = ["last_enqueue_time"])]) data class WorkSpec(// 1.任務(wù)執(zhí)行的狀態(tài),ENQUEUED/RUNNING/SUCCEEDED/FAILED/CANCELLED@JvmField@ColumnInfo(name = "state")var state: WorkInfo.State = WorkInfo.State.ENQUEUED,// 2.Worker的類名,便于反射和日志打印@JvmField@ColumnInfo(name = "worker_class_name")var workerClassName: String,// 3.輸入?yún)?shù)@JvmField@ColumnInfo(name = "input")var input: Data = Data.EMPTY,// 4.輸出參數(shù)@JvmField@ColumnInfo(name = "output")var output: Data = Data.EMPTY,// 5.定時(shí)任務(wù)@JvmField@ColumnInfo(name = "initial_delay")var initialDelay: Long = 0,// 6.定期任務(wù)@JvmField@ColumnInfo(name = "interval_duration")var intervalDuration: Long = 0,// 7.約束關(guān)系@JvmField@Embeddedvar constraints: Constraints = Constraints.NONE,// ... )設(shè)計(jì)好字段后,接下來(lái)我們?cè)O(shè)計(jì)其操作類 WorkSpecDao :
@Dao interface WorkSpecDao {// ...@Insert(onConflict = OnConflictStrategy.IGNORE)fun insertWorkSpec(workSpec: WorkSpec)@Query("SELECT * FROM workspec WHERE id=:id")fun getWorkSpec(id: String): WorkSpec?@Query("SELECT id FROM workspec")fun getAllWorkSpecIds(): List<String>@Query("UPDATE workspec SET state=:state WHERE id=:id")fun setState(state: WorkInfo.State, id: String): Int@Query("SELECT state FROM workspec WHERE id=:id")fun getState(id: String): WorkInfo.State?// ... }細(xì)心的讀者會(huì)發(fā)現(xiàn),WorkSpecDao 的設(shè)計(jì)中除了聲明常規(guī)的 Insert、Query、Update 等,并沒(méi)有 DELETE 類型的操作。
讀者經(jīng)過(guò)認(rèn)真考慮后,可得該設(shè)計(jì)是合理的——由于有 setState() 可更新任務(wù)的狀態(tài),已完成或取消的工作無(wú)需刪除,而是通過(guò) SQL 語(yǔ)句,靈活分類按需獲取,如:
@Dao interface WorkSpecDao {// ...// 獲取全部執(zhí)行中的任務(wù)@Query("SELECT * FROM workspec WHERE state=RUNNING")fun getRunningWork(): List<WorkSpec>// 獲取全部近期已完成的任務(wù)@Query("SELECT * FROM workspec WHERE last_enqueue_time >= :startingAt AND state IN COMPLETED_STATES ORDER BY last_enqueue_time DESC")fun getRecentlyCompletedWork(startingAt: Long): List<WorkSpec>// ... }這可稱之額外收獲,通過(guò) Room 的持久化存儲(chǔ),在保證了任務(wù)能夠被穩(wěn)定執(zhí)行的同時(shí),還可對(duì)所有任務(wù)進(jìn)行備份,從而向開(kāi)發(fā)者提供更多額外的能力。
準(zhǔn)確來(lái)說(shuō),WorkManager 內(nèi)部的 Dao 提供了 Delete 方法,但并未直接暴露給開(kāi)發(fā)者,而是用于內(nèi)部解決 任務(wù)間的沖突 問(wèn)題,這個(gè)后文再提。
優(yōu)先級(jí)管理
下面我們針對(duì)任務(wù)的 優(yōu)先級(jí) 進(jìn)一步進(jìn)行討論。
雖然上文明確說(shuō)了,對(duì)于需要立即執(zhí)行的行為,不應(yīng)該作為后臺(tái)任務(wù),而是應(yīng)該直接執(zhí)行對(duì)應(yīng)的業(yè)務(wù)代碼塊——看起來(lái)優(yōu)先級(jí)機(jī)制并非剛需。
但實(shí)際上,這種機(jī)制仍然有一定的必要性。
1.約束條件
說(shuō)到 約束條件,熟悉 JobScheduler 的開(kāi)發(fā)者對(duì)此不會(huì)感到陌生,
| NetworkType | 約束運(yùn)行工作所需的網(wǎng)絡(luò)類型。例如 Wi-Fi (UNMETERED)。 |
| BatteryNotLow | 如果設(shè)置為 true,那么當(dāng)設(shè)備處于“電量不足模式”時(shí),工作不會(huì)運(yùn)行。 |
| RequiresCharging | 如果設(shè)置為 true,那么工作只能在設(shè)備充電時(shí)運(yùn)行。 |
| DeviceIdle | 如果設(shè)置為 true,則要求用戶的設(shè)備必須處于空閑狀態(tài),才能運(yùn)行工作。在運(yùn)行批量操作時(shí),此約束會(huì)非常有用;若是不用此約束,批量操作可能會(huì)降低用戶設(shè)備上正在積極運(yùn)行的其他應(yīng)用的性能。 |
| StorageNotLow | 如果設(shè)置為 true,那么當(dāng)用戶設(shè)備上的存儲(chǔ)空間不足時(shí),工作不會(huì)運(yùn)行。 |
WorkManager 也提供了類似的概念,實(shí)際上內(nèi)部也正是基于 JobScheduler 實(shí)現(xiàn)的,但 WorkManager 并非只是單純的代理。
首先,當(dāng)API版本不足時(shí),WorkManager 兼容性使用 AlarmManager 或 GcmScheduler 作為補(bǔ)充:
// androidx.work.impl.Schedulers.java static Scheduler createBestAvailableBackgroundScheduler(@NonNull Context context,@NonNull WorkManagerImpl workManager) {Scheduler scheduler;if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {scheduler = new SystemJobScheduler(context, workManager);} else {scheduler = tryCreateGcmBasedScheduler(context);if (scheduler == null) {scheduler = new SystemAlarmScheduler(context);}}return scheduler; }其次,讀者知道,JobScheduler 的 onStartJob 等回調(diào)默認(rèn)運(yùn)行在主線程,不能直接進(jìn)行耗時(shí)操作。WorkManager 的 Worker 內(nèi)部則進(jìn)行了線程調(diào)度,默認(rèn)實(shí)現(xiàn)在 WorkerThread:
// androidx.work.impl.background.systemjob.SystemJobService public class SystemJobService extends JobService {public boolean onStartJob(@NonNull JobParameters params) {//...mWorkManagerImpl.startWork(...);} }// androidx.work.impl.WorkManagerImpl public class WorkManagerImpl extends WorkManager {public void startWork(...) {mWorkTaskExecutor.executeOnTaskThread(new StartWorkRunnable(...));} }由于 JobScheduler 是由系統(tǒng)服務(wù)中的 JobSchedulerService 實(shí)現(xiàn)的,因此其自身的高權(quán)限,可以在APP被殺或重啟后,仍然可以喚起并執(zhí)行 JobService 及對(duì)應(yīng)的任務(wù)。
2.新的保活機(jī)制?
Android 官方提供了后臺(tái)作業(yè)的強(qiáng)大支持,國(guó)內(nèi)廠商大多數(shù)第一時(shí)間卻想拿它來(lái)做 保活。
——舉例來(lái)說(shuō),既然 WorkManager 支持定期任務(wù),且即使 APP 被殺或者重啟都能夠保證執(zhí)行;那么我一個(gè) IM APP,每 10 秒拉取下接口看有沒(méi)有新消息,順便啟動(dòng)下 APP 某些頁(yè)面或者通知組件,想必也是非常合理的。
實(shí)際上,對(duì)于 保活 的訴求,WorkManager 做不到,其本質(zhì)是 JobScheduler 做不到:
首先,隨著版本的迭代,Android 系統(tǒng)對(duì)后臺(tái)任務(wù)的管理愈發(fā)嚴(yán)苛,小于 15 分鐘的定期任務(wù)已經(jīng)被強(qiáng)制調(diào)整為 15 分鐘執(zhí)行,避免頻繁的后臺(tái)定時(shí)任務(wù)對(duì)前臺(tái)應(yīng)用的影響,規(guī)避了 API 的非法濫用:
WorkManager : 我把你當(dāng)兄弟,你竟然想睡我?
其次是筆者的猜測(cè),由于用戶安全等相關(guān)的考量,國(guó)內(nèi)設(shè)備廠商對(duì) JobSchedulerService 等相關(guān)都有一定的魔改——比如,當(dāng)用戶手動(dòng)將 APP 強(qiáng)制關(guān)閉,這種操作意圖擁有最高優(yōu)先級(jí),即使是系統(tǒng)服務(wù)也不應(yīng)對(duì)其再次啟動(dòng)(廠商白名單除外,如微信和QQ?)。
兩點(diǎn)結(jié)合,WorkManager 的定期任務(wù)受到了嚴(yán)格的限制,這也意味著類似保活需求其無(wú)法滿足,其 “不穩(wěn)定” 性這也是其國(guó)內(nèi)應(yīng)用較少的主要原因之一。
3.前臺(tái)服務(wù)
最后,我們討論下 如何規(guī)范地調(diào)度系統(tǒng)資源 。
最經(jīng)典的場(chǎng)景仍然是后臺(tái)任務(wù)的加急,即使有約束條件,部分后臺(tái)任務(wù)仍需要被 特殊加急 ,比如 用戶聊天時(shí)發(fā)送短視頻、處理付款或訂閱流程 等。這些任務(wù)對(duì)用戶很重要,會(huì)在后臺(tái)快速執(zhí)行,并需要立即開(kāi)始執(zhí)行。
系統(tǒng)對(duì)于應(yīng)用的資源分配非常嚴(yán)格,讀者可以通過(guò) 這里 簡(jiǎn)單了解。
由于任務(wù)的執(zhí)行依賴于系統(tǒng)對(duì)資源的分配,因此想要提高執(zhí)行的優(yōu)先級(jí),勢(shì)必需要提升 APP 組件自身的優(yōu)先級(jí),那么實(shí)現(xiàn)方案已經(jīng)非常明顯了:使用 前臺(tái)服務(wù) 。
當(dāng)你需要通過(guò)調(diào)用類似 worker.setExpedited(true) 標(biāo)記為加急任務(wù)時(shí),Worker 內(nèi)部的具體實(shí)現(xiàn),需開(kāi)發(fā)者額外創(chuàng)建前臺(tái)通知,提升優(yōu)先級(jí)的同時(shí),將你后臺(tái)的任務(wù)行為同步給用戶。
小結(jié)
小結(jié)并不是總結(jié),還有更多內(nèi)容可以擴(kuò)展,比如:
-
1、Android 系統(tǒng)的 JobScheduler 任務(wù)調(diào)度機(jī)制的內(nèi)部原理是什么樣的?
-
2、WorkManager 組件的初始化機(jī)制、持久化機(jī)制等,內(nèi)部的實(shí)現(xiàn)細(xì)節(jié)有哪些需要注意的?
-
3、WorkManager 中的約束任務(wù)和非約束任務(wù)在執(zhí)行上有什么不同?
-
4、WorkManager 還有哪些其它的亮點(diǎn)?
篇幅原因,這些問(wèn)題筆者將另起一篇進(jìn)行更深入性的討論,敬請(qǐng)期待。
關(guān)于我
Hello,我是 卻把清梅嗅 ,如果您覺(jué)得文章對(duì)您有價(jià)值,歡迎 ??,也歡迎關(guān)注我的 博客 或者 GitHub。
如果您覺(jué)得文章還差了那么點(diǎn)東西,也請(qǐng)通過(guò) 關(guān)注 督促我寫(xiě)出更好的文章——萬(wàn)一哪天我進(jìn)步了呢?
- 我的Android學(xué)習(xí)體系
- 關(guān)于文章糾錯(cuò)
- 關(guān)于知識(shí)付費(fèi)
- 關(guān)于《反思》系列
總結(jié)
以上是生活随笔為你收集整理的社区说|浅谈 WorkManager 的设计与实现:系统概述的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 9 机器学习 支持向量机
- 下一篇: host 计算机英语作文,My Favo