Q: 什么是WorkManager蒙幻?
WorkManager 是一個(gè) Android Jetpack 庫从撼,當(dāng)滿足工作的約束條件時(shí)怜森,用來運(yùn)行可延遲、需要保障的后臺(tái)工作暇咆,即使應(yīng)用程序退出锋爪,系統(tǒng)也會(huì)運(yùn)行它們。
Q:WorkManager的適用場景糯崎?
1、可延遲進(jìn)行的任務(wù)
a.滿足某些條件才執(zhí)行的任務(wù)河泳,如需要在充電時(shí)才執(zhí)行的任務(wù)沃呢。 b.用戶無感知或可延遲感知的任務(wù),如同步配置信息拆挥,同步資源薄霜,同步通訊錄等某抓。
2、定期重復(fù)性任務(wù)惰瓜,但時(shí)效性要求不高的否副,如定期 log 上傳,數(shù)據(jù)備份等崎坊。
3备禀、退出應(yīng)用后還應(yīng)繼續(xù)執(zhí)行的未完成任務(wù)。
Q:WorkManager的好處奈揍?
- 處理不同系統(tǒng)版本的兼容性
- 遵循系統(tǒng)健康最佳實(shí)踐
- 支持異步一次性和周期性任務(wù)
- 支持帶輸入/輸出的鏈?zhǔn)饺蝿?wù)
- 允許你設(shè)置在任務(wù)運(yùn)行時(shí)的約束
- 即使應(yīng)用程序或設(shè)備重啟曲尸,也可以保證任務(wù)執(zhí)行
Q:WorkManager的特點(diǎn)?
保證任務(wù)一定會(huì)被執(zhí)行
WorkManager 有自己的數(shù)據(jù)庫男翰,每一個(gè)任務(wù)的信息與任務(wù)狀態(tài)另患,都會(huì)保存在本地?cái)?shù)據(jù)庫中。所以即使程序沒有在運(yùn)行蛾绎,或者在設(shè)備重啟等情況下昆箕,WorkManager 依然可以保證任務(wù)的執(zhí)行,只是不保證任務(wù)立即被執(zhí)行租冠。
合理使用設(shè)備資源
在執(zhí)行很多周期性或非立即執(zhí)行的任務(wù)時(shí)鹏倘,WorkManager 提供我們 API,幫助我們合理利用設(shè)備資源肺稀,避免不必要的內(nèi)存第股,流量,電量等消耗话原。
Q:WorkManager的初始化夕吻?
1、WorkManager 的初始化是在 app 冷啟動(dòng)后繁仁,由 WorkManagerInitializer 這個(gè) ContentProvider 執(zhí)行的涉馅。
2、初始化過程包含了 Configuration黄虱,WorkManagerTaskExecutor稚矿,WorkDatabase,Schedulers捻浦,Processor 等的初始化過程晤揣。
Schedulers 有兩個(gè)。
(1) GreedyScheduler: 執(zhí)行沒有任何約束的非周期性的任務(wù)朱灿。
(2)SystemJobScheduler/GcmBasedScheduler/SystemAlarmScheduler: 執(zhí)行周期性或者有約束性的任務(wù)昧识。優(yōu)先返回 SystemJobScheduler,在 build version 小于 23 的情況下先嘗試返回 GcmBasedScheduler盗扒,若返回為空再返回 SystemAlarmScheduler跪楞。
3缀去、初始化的最后,會(huì)根據(jù)情況找到需要被執(zhí)行的任務(wù)進(jìn)行調(diào)度執(zhí)行甸祭。
Q: 不帶約束條件的任務(wù)執(zhí)行流程缕碎?
1、 在 WorkManager 執(zhí)行了 enqueue() 后池户,創(chuàng)建 WorkContinuationImpl 對象執(zhí)行 enqueue() 方法咏雌。
2、WorkContinuationImpl 持有的 EnqueueRunnable 對象將任務(wù)添加到 db煞檩,并交給 Schedulers 去調(diào)度处嫌。
3、Schedulers 將任務(wù)交給每一個(gè) Scheduler 去處理斟湃。假設(shè)是GreedyScheduler 會(huì)先處理這個(gè)任務(wù)熏迹。
4、GreedyScheduler 經(jīng)過一系列判斷后凝赛,調(diào)用 WorkManager 的 startWork() 方法執(zhí)行這種一次性注暗,非延遲,無約束的任務(wù)墓猎。
5捆昏、WorkManager 持有的 StartWorkRunnable 對象會(huì)將任務(wù)交給 Processor 去處理,執(zhí)行 startWork() 方法毙沾。
6骗卜、Processor 創(chuàng)建一個(gè) WorkerWrapper 對象,由它去調(diào)用 Worker 的 startWork() 方法左胞,執(zhí)行我們自定義 worker 的任務(wù)寇仓,并返回相應(yīng)的 result。
7烤宙、任務(wù)完成后遍烦,WorkerWrapper 會(huì)根據(jù) result 對任務(wù)狀態(tài),db 等進(jìn)行更新躺枕,然后 schedule 下一個(gè)任務(wù)服猪。
Q:帶約束條件的任務(wù)執(zhí)行過程?
在任務(wù)執(zhí)行的過程中拐云,由于增加了約束條件罢猪,常駐的 GreedyScheduler 的 schedule() 方法將不會(huì) startWork()核行,而是根據(jù) build version 交由 SystemJobScheduler 或 SystemAlarmScheduler 進(jìn)行處理司顿。
SystemJobScheduler (Build Version >=23)
合理使用設(shè)備資源
>> 在執(zhí)行很多周期性或非立即執(zhí)行的任務(wù)時(shí)叮趴,WorkManager 提供我們 API纠吴,幫助我們合理利用設(shè)備資源,避免不必要的內(nèi)存贤笆,流量挤安,電量等消耗挡篓。
Q:WorkManager的初始化捅暴?
> 1恬砂、WorkManager 的初始化是在 app 冷啟動(dòng)后,由 WorkManagerInitializer 這個(gè) ContentProvider 執(zhí)行的蓬痒。
>2泻骤、初始化過程包含了 Configuration,WorkManagerTaskExecutor梧奢,WorkDatabase狱掂,Schedulers,Processor 等的初始化過程亲轨。
Schedulers 有兩個(gè)趋惨。
>(1) GreedyScheduler: 執(zhí)行沒有任何約束的非周期性的任務(wù)。
(2)SystemJobScheduler/GcmBasedScheduler/SystemAlarmScheduler: 執(zhí)行周期性或者有約束性的任務(wù)惦蚊。優(yōu)先返回 SystemJobScheduler器虾,在 build version 小于 23 的情況下先嘗試返回 GcmBasedScheduler,若返回為空再返回 SystemAlarmScheduler蹦锋。
>3兆沙、初始化的最后,會(huì)根據(jù)情況找到需要被執(zhí)行的任務(wù)進(jìn)行調(diào)度執(zhí)行莉掂。
Q: 不帶約束條件的任務(wù)執(zhí)行流程葛圃?
>1、 在 WorkManager 執(zhí)行了 enqueue() 后憎妙,創(chuàng)建 WorkContinuationImpl 對象執(zhí)行 enqueue() 方法库正。
> 2、WorkContinuationImpl 持有的 EnqueueRunnable 對象將任務(wù)添加到 db尚氛,并交給 Schedulers 去調(diào)度诀诊。
>3、Schedulers 將任務(wù)交給每一個(gè) Scheduler 去處理阅嘶。假設(shè)是GreedyScheduler 會(huì)先處理這個(gè)任務(wù)属瓣。
>4、GreedyScheduler 經(jīng)過一系列判斷后讯柔,調(diào)用 WorkManager 的 startWork() 方法執(zhí)行這種一次性抡蛙,非延遲,無約束的任務(wù)魂迄。
5粗截、WorkManager 持有的 StartWorkRunnable 對象會(huì)將任務(wù)交給 Processor 去處理,執(zhí)行 startWork() 方法捣炬。
6熊昌、Processor 創(chuàng)建一個(gè) WorkerWrapper 對象绽榛,由它去調(diào)用 Worker 的 startWork() 方法,執(zhí)行我們自定義 worker 的任務(wù)婿屹,并返回相應(yīng)的 result灭美。
7、任務(wù)完成后昂利,WorkerWrapper 會(huì)根據(jù) result 對任務(wù)狀態(tài)届腐,db 等進(jìn)行更新,然后 schedule 下一個(gè)任務(wù)蜂奸。
Q:帶約束條件的任務(wù)執(zhí)行過程犁苏?
> 在任務(wù)執(zhí)行的過程中,由于增加了約束條件扩所,常駐的 GreedyScheduler 的 schedule() 方法將不會(huì) startWork()围详,而是根據(jù) build version 交由 SystemJobScheduler 或 SystemAlarmScheduler 進(jìn)行處理。
>>SystemJobScheduler (Build Version >=23)
在SystemJobScheduler的構(gòu)造函數(shù)中祖屏,會(huì)獲取JobScheduler對象短曾,并傳入了一個(gè)SystemJobInfoConverter對象( 用來將我們的workSpecs轉(zhuǎn)化為JobInfo,這個(gè)JobInfo是jobScheduler需要的參數(shù) 赐劣,用來描述任務(wù)的執(zhí)行時(shí)間嫉拐,條件,策略等一系列的行為魁兼。)婉徘,在SystemJobScheduler 的 schedule() 方法執(zhí)行了 scheduleInternal();
JobScheduler是系統(tǒng)服務(wù)咐汞,由android程序啟動(dòng)的時(shí)候自動(dòng)拉起無需手動(dòng)去操作,而與JobScheduler對應(yīng)的jobservice由系統(tǒng)自動(dòng)生成盖呼,這里的JobService是SystemJobService,當(dāng)JobScheduler中的條件滿足時(shí)化撕,SystemJobService就會(huì)被調(diào)用到几晤。
在 SystemJobService中的onStartJob中,重點(diǎn)是最后一個(gè)方法植阴,該方法調(diào)用了WorkManagerImpl.startWork蟹瘾。
SystemAlarmScheduler(Build Version < 23)
SystemAlarmScheduler 使用的是 AlarmManager 來調(diào)度執(zhí)行任務(wù)。
在 AndroidManifest 里有如下 receiver 注冊:
<receiver android:directBootAware="false" android:enabled="false" android:exported="false" android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy">
<intent-filter>
<action android:name="android.intent.action.BATTERY_OKAY"/>
<action android:name="android.intent.action.BATTERY_LOW"/>
</intent-filter>
</receiver>
在電量變化時(shí)掠手,收到 BATTERY_LOW 的廣播憾朴。在 BatteryNotLowProxy 的 onReceive() 進(jìn)行處理:
//ConstraintProxy
public static class BatteryNotLowProxy extends ConstraintProxy {
}
@Override
public void onReceive(Context context, Intent intent) {
Logger.get().debug(TAG, String.format("onReceive : %s", intent));
Intent constraintChangedIntent = CommandHandler.createConstraintsChangedIntent(context);
context.startService(constraintChangedIntent);
}
createConstraintsChangedIntent() 的執(zhí)行如下:
//ConstraintProxy
static Intent createConstraintsChangedIntent(@NonNull Context context) {
Intent intent = new Intent(context, SystemAlarmService.class);
intent.setAction(ACTION_CONSTRAINTS_CHANGED);
return intent;
}
SystemAlarmService 的 onStartCommand() 處理如下:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
... ...
if (intent != null) {
mDispatcher.add(intent, startId);
}
// If the service were to crash, we want all unacknowledged Intents to get redelivered.
return Service.START_REDELIVER_INTENT;
}
調(diào)用了 SystemAlarmDispatcher.add() 方法。
//SystemAlarmDispatcher
@MainThread
public boolean add(@NonNull final Intent intent, final int startId) {
... ...
if (CommandHandler.ACTION_CONSTRAINTS_CHANGED.equals(action)
&& hasIntentWithAction(CommandHandler.ACTION_CONSTRAINTS_CHANGED)) {
return false;
}
intent.putExtra(KEY_START_ID, startId);
synchronized (mIntents) {
boolean hasCommands = !mIntents.isEmpty();
mIntents.add(intent);
if (!hasCommands) {
// Only call processCommand if this is the first command.
// The call to dequeueAndCheckForCompletion will process the remaining commands
// in the order that they were added.
processCommand();
}
}
return true;
}
add() 方法中執(zhí)行了 processCommand()喷鸽,這段代碼的核心執(zhí)行語句是:
//SystemAlarmDispatcher
mCommandHandler.onHandleIntent(mCurrentIntent, startId,
SystemAlarmDispatcher.this);
在 CommandHandler 的 onHandleIntent() 方法中众雷,action 為 ACTION_CONSTRAINTS_CHANGED 的執(zhí)行是:
//CommandHandler
if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
handleConstraintsChanged(intent, startId, dispatcher);
}
//CommandHandler
private void handleConstraintsChanged(
@NonNull Intent intent, int startId,
@NonNull SystemAlarmDispatcher dispatcher) {
Logger.get().debug(TAG, String.format("Handling constraints changed %s", intent));
// Constraints changed command handler is synchronous. No cleanup
// is necessary.
ConstraintsCommandHandler changedCommandHandler =
new ConstraintsCommandHandler(mContext, startId, dispatcher);
changedCommandHandler.handleConstraintsChanged();
}
在 handleConstraintsChanged() 方法的執(zhí)行中,會(huì)創(chuàng)建一個(gè) action 為 ACTION_DELAY_MET 的 Intent 然后由 SystemAlarmDispatcher 發(fā)送出去,實(shí)際上也是調(diào)用了 SystemAlarmDispatcher.add() 方法砾省〖Ω冢回到 SystemAlarmDispatcher 的 add() 流程。
//ConstraintsCommandHandler
Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId);
Logger.get().debug(TAG, String.format(
"Creating a delay_met command for workSpec with id (%s)", workSpecId));
mDispatcher.postOnMainThread(
new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));
回到 onHandleIntent() 方法编兄,在 CommandHandler 的 onHandleIntent() 方法中纤房,action 為 ACTION_DELAY_MET 的執(zhí)行是:
//CommandHandler
else if (ACTION_DELAY_MET.equals(action)) {
handleDelayMet(intent, startId, dispatcher);
}
handleDelayMet() 的執(zhí)行過程,會(huì)調(diào)用 DelayMetCommandHandler 的 handleProcessWork() 方法翻诉,接著執(zhí)行 onAllConstraintsMet():
@Override
public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
... ...
synchronized (mLock) {
if (mCurrentState == STATE_INITIAL) {
... ...
boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);
... ...
} else {
Logger.get().debug(TAG, String.format("Already started work for %s", mWorkSpecId));
}
}
}
到這里終于看到由 SystemAlarmDispatcher 調(diào)用了 Processor 的 startWork() 方法,回到了之前章節(jié)分析的任務(wù)執(zhí)行流程捌刮。到此為止碰煌,一個(gè)任務(wù)在不同條件下的創(chuàng)建,執(zhí)行流程就分析完畢绅作。
Q:WorkManager在設(shè)備重啟后如何保證任務(wù)可以得到執(zhí)行芦圾?
WorkManager在初始化時(shí)創(chuàng)建了Room數(shù)據(jù)庫, 將任務(wù)列表序列化到本地俄认,記錄每一個(gè)任務(wù)的屬性个少,執(zhí)行條件,執(zhí)行順序及執(zhí)行狀態(tài)等眯杏。從而保證任務(wù)在冷啟動(dòng)或硬件重啟后夜焦,可以根據(jù)條件繼續(xù)執(zhí)行。
WorkManager 流程分析和源碼解析 | 開發(fā)者說·DTalk
](https://mp.weixin.qq.com/s?__biz=MzAwODY4OTk2Mg==&mid=2652069060&idx=2&sn=77398948c657a70aeae4db35f8042a35&chksm=808cfc81b7fb759727b3b96d41addb3d6bb5f8793c064fb295631f0be496a4567ba75214dd3d&mpshare=1&scene=23&srcid=1107uIVkYdQ2fu0qgbADCEBW&sharer_sharetime=1604719995685&sharer_shareid=8206e3713102e9ca474649d51eda190c%23rd)