一 使用入門
我們先了解WorkManager工作的流程:
- 誰來做? 定義一個(gè)負(fù)責(zé)工作的Worker
public class SimpleWorker extends Worker {
@NonNull
@Override
public Result doWork() {
// return Result.failure(); // 執(zhí)行失敗
// return Result.retry(); // 重試
// return Result.success(); // 執(zhí)行成功
return null; // 執(zhí)行結(jié)束?
}
}
Worker是WorkManager最終實(shí)現(xiàn)任務(wù)的工人,它不用管會(huì)在什么實(shí)際執(zhí)行任務(wù),被安排怎樣執(zhí)行任務(wù),只管處理任務(wù)doWork并根據(jù)任務(wù)執(zhí)行情況反饋任務(wù)結(jié)果return Result。
Worker執(zhí)行結(jié)果Result:表示任務(wù)狀態(tài),他一般有failure、retry、success三個(gè)狀態(tài),且failure、success可以攜帶數(shù)據(jù)Data。
2.怎么做 ?定義制定工作方案的WorkRequest
這里就有兩種request,一種是執(zhí)行一次的,一種是重復(fù)執(zhí)行的。
非重復(fù)性工作: 工人應(yīng)該一次性把工作做掉
OneTimeWorkRequest workRequest1 = OneTimeWorkRequest.from(SimpleWorker.class);
周期任務(wù): 工人應(yīng)該每隔多久做一次工作
PeriodicWorkRequest request = new PeriodicWorkRequest
.Builder(SimpleWorker.class, 16, TimeUnit.MINUTES).build();
注意: 關(guān)于周期任務(wù)有一個(gè)霸王條款:
注意:可以定義的最短重復(fù)間隔是 15 分鐘(與 JobScheduler API 相同)。
2.1 WorkRequest 目前有兩個(gè)子類,分別為單次執(zhí)行任務(wù) OneTimeWorkRequest和周期執(zhí)行任務(wù) PeriodicWorkRequest. 他們的主要區(qū)別是任務(wù)執(zhí)行方案不同導(dǎo)致的狀態(tài)變化不同。
[簡單單次執(zhí)行任務(wù) OneTimeWorkRequest 工作狀態(tài)圖]

對(duì)于 one-time 工作請(qǐng)求,工作的初始狀態(tài)為 ENQUEUED。
在 ENQUEUED 狀態(tài)下,您的工作會(huì)在滿足其 Constraints 和初始延遲計(jì)時(shí)要求后立即運(yùn)行。接下來,該工作會(huì)轉(zhuǎn)為 RUNNING 狀態(tài),然后可能會(huì)根據(jù)工作的結(jié)果轉(zhuǎn)為 SUCCEEDED、FAILED 狀態(tài);或者,如果結(jié)果是 retry,它可能會(huì)回到 ENQUEUED 狀態(tài)。在此過程中,隨時(shí)都可以取消工作,取消后工作將進(jìn)入 CANCELLED 狀態(tài)。
周期執(zhí)行任務(wù) PeriodicWorkRequest 工作狀態(tài)圖

周期性工作的初始狀態(tài)為 ENQUEUED,正常執(zhí)行情況下,周期性工作的狀態(tài)在ENQUEUED ?RUNNING之間交替,知道任務(wù)被取消時(shí),狀態(tài)為CANCELLED。
成功和失敗狀態(tài)僅適用于一次性工作和鏈?zhǔn)焦ぷ?。定期工作只有一個(gè)終止?fàn)顟B(tài) CANCELLED。這是因?yàn)槎ㄆ诠ぷ饔肋h(yuǎn)不會(huì)結(jié)束。每次運(yùn)行后,無論結(jié)果如何,系統(tǒng)都會(huì)重新對(duì)其進(jìn)行調(diào)度。
上面講了一些簡單工作執(zhí)行。下來看看他還有什么可以操作的地方。
二 任務(wù)監(jiān)聽
務(wù)進(jìn)度)
上面介紹了工作的狀態(tài),我們?cè)撊绾伪O(jiān)聽這些狀態(tài)呢?
在將工作加入隊(duì)列后,您可以隨時(shí)按其 name、id 或與其關(guān)聯(lián)的 tag 在 WorkManager 中進(jìn)行查詢,以檢查其狀態(tài)
// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>
// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>
// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>
該查詢會(huì)返回 WorkInfo對(duì)象的 ListenableFuture,該值包含工作的 id、其標(biāo)記、其當(dāng)前的 State 以及通過 Result.success(outputData)設(shè)置的任何輸出數(shù)據(jù)。
利用每個(gè)方法的 LiveData變種,您可以通過注冊(cè)監(jiān)聽器來觀察 WorkInfo 的變化。例如,如果您想要在某項(xiàng)工作成功完成后向用戶顯示消息,您可以進(jìn)行如下設(shè)置:
workManager.getWorkInfoByIdLiveData(syncWorker.id)
.observe(getViewLifecycleOwner(), workInfo -> {
if (workInfo.getState() != null &&
workInfo.getState() == WorkInfo.State.SUCCEEDED) {
Snackbar.make(requireView(),
R.string.work_completed, Snackbar.LENGTH_SHORT)
.show();
}
});
復(fù)雜的工作查詢
WorkManager 2.4.0 及更高版本支持使用 WorkQuery對(duì)象對(duì)已加入隊(duì)列的作業(yè)進(jìn)行復(fù)雜查詢。WorkQuery 支持按工作的標(biāo)記、狀態(tài)和唯一工作名稱的組合進(jìn)行查詢。
以下示例說明了如何查找?guī)в小皊yncTag”標(biāo)記、處于 FAILED 或 CANCELLED 狀態(tài),且唯一工作名稱為“preProcess”或“sync”的所有工作。
WorkQuery workQuery = WorkQuery.Builder
.fromTags(Arrays.asList("syncTag"))
.addStates(Arrays.asList(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
.addUniqueWorkNames(Arrays.asList("preProcess", "sync")
)
.build();
ListenableFuture<List<WorkInfo>> workInfos = workManager.getWorkInfos(workQuery);
取消和停止工作
如果您不再需要運(yùn)行先前加入隊(duì)列的工作,則可以要求將其取消。您可以按工作的 name、id 或與其關(guān)聯(lián)的 tag 取消工作。
// by id
workManager.cancelWorkById(syncWorker.id);
// by name
workManager.cancelUniqueWork("sync");
// by tag
workManager.cancelAllWorkByTag("syncTag");
傳遞數(shù)據(jù)
在一些業(yè)務(wù)場(chǎng)景下,我們需要向任務(wù)中傳遞數(shù)據(jù),可以使用androidx.work.Data向WorkRequest中添加數(shù)據(jù)。
// 創(chuàng)建需傳遞的數(shù)據(jù)
Data data = new Data.Builder().putString("message", "MainActivity").build();
// 單次執(zhí)行任務(wù)
OneTimeWorkRequest request1 = new OneTimeWorkRequest.Builder(Task4DataWorker.class)
// 向WorkRequest中添加數(shù)據(jù)
.setInputData(data).build();
// 將任務(wù)加入隊(duì)列
WorkManager.getInstance(this).enqueue(request1);
需要注意的是,Data源碼可知,此處傳遞數(shù)據(jù)僅支持基礎(chǔ)數(shù)據(jù)類型及其封裝、String以及上述類型的數(shù)組
有傳遞數(shù)據(jù)自然有獲取數(shù)據(jù),我們可以Worker中通過getInputData()獲取
傳入的Data對(duì)象,通過Data獲取傳入的值,也可以通過Data和Result將值傳遞給觀察者。
public class DataWorker extends Worker {
public DataWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@SuppressLint("RestrictedApi")
@NonNull
@Override
public Result doWork() {
String message = getInputData().getString("message");
Data myMsg = new Data.Builder().putString("message", "message from Worker").build();
return new Result.Success(myMsg);
}
}
而從Worker中傳出的Data則可在WorkRequest的工作狀態(tài)WorkInfo中取得:
WorkManager.getInstance(context)
.getWorkInfoByIdLiveData(workRequest.getId())
.observe(context, workInfo -> {
if (workInfo!=null){
if (workInfo.getState().isFinished()) {
String message = workInfo.getOutputData().getString("message");
}
}
});
多任務(wù)串聯(lián)
除一般的單一任務(wù)的場(chǎng)景外,我們面對(duì)的業(yè)務(wù)往往也要處理多個(gè)任務(wù)的場(chǎng)景,此時(shí)我們可以串聯(lián)多個(gè)任務(wù),也可以將多個(gè)任務(wù)編組成任務(wù)鏈去使用:
WorkManager.getInstance(this).beginWith(workRequest2)
.then(workRequest3)
.then(workRequest4)
.enqueue();
// or
WorkContinuation continuation = WorkManager.getInstance(this)
.beginWith(workRequest2)
.then(workRequest3)
.then(workRequest4);
continuation.then(workRequest).enqueue();
唯一任務(wù)
唯一工作是一個(gè)很實(shí)用的概念,可確保同一時(shí)刻只有一個(gè)具有特定名稱的工作實(shí)例。與 ID 不同的是,唯一名稱是人類可讀的,由開發(fā)者指定,而不是由 WorkManager 自動(dòng)生成。與標(biāo)記不同,唯一名稱僅與一個(gè)工作實(shí)例相關(guān)聯(lián)。
唯一工作既可用于一次性工作,也可用于定期工作。您可以通過調(diào)用以下方法之一創(chuàng)建唯一工作序列,具體取決于您是調(diào)度重復(fù)工作還是一次性工作。WorkManager.enqueueUniqueWork()(用于一次性工作)
WorkManager.enqueueUniquePeriodicWork()(用于定期工作)
這兩種方法都接受 3 個(gè)參數(shù):
uniqueWorkName - 用于唯一標(biāo)識(shí)工作請(qǐng)求的 String。
existingWorkPolicy - 此 enum 可告知 WorkManager:如果已有使用該名稱且尚未完成的唯一工作鏈,應(yīng)執(zhí)行什么操作。
OneTimeWorkRequest workRequest2 = new OneTimeWorkRequest.Builder(SimpleWorker2.class).build();
WorkManager.getInstance(this).beginUniqueWork("SimpleWorker",
ExistingWorkPolicy.REPLACE, workRequest)
// ExistingWorkPolicy.APPEND, workRequest)
// ExistingWorkPolicy.KEEP, workRequest)
.then(workRequest2)
// .then(workRequest)
.enqueue();
此處定義的唯一,僅在任務(wù)正在執(zhí)行且出現(xiàn)相同uniqueWorkName名稱時(shí),existingWorkPolicy才生效,無法影響已結(jié)束的同名任務(wù)(此同名僅與uniqueWorkName有關(guān))。
以定義兩個(gè)相同uniqueWorkName的WorkRequest為例,來觀察existingWorkPolicy值的作用及影響
任務(wù)約束
Constraints可確保將工作延遲到滿足最佳條件時(shí)運(yùn)行。以下約束適用于 WorkManager。
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)行批量操作,否則可能會(huì)降低用戶設(shè)備上正在積極運(yùn)行的其他應(yīng)用的性能,建議您使用此約束。
StorageNotLow 如果設(shè)置為 true,那么當(dāng)用戶設(shè)備上的存儲(chǔ)空間不足時(shí),工作不會(huì)運(yùn)行
任務(wù)延時(shí)
如果工作沒有約束,或者當(dāng)工作加入隊(duì)列時(shí)所有約束都得到了滿足,那么系統(tǒng)可能會(huì)選擇立即運(yùn)行該工作。如果您不希望工作立即運(yùn)行,可以將工作指定為在經(jīng)過一段最短初始延遲時(shí)間后再啟動(dòng)。
重試和退避政策
若Worker返回結(jié)果Result.retry()時(shí),觸發(fā)重試退避政策,即下次調(diào)度Worker應(yīng)在多長時(shí)間以后,支持設(shè)置退避時(shí)間基數(shù)和基數(shù)遞增方式,遞增方式目前支持線性LINEAR和指數(shù)EXPONENTIAL
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(RetryWorker.class)
.setBackoffCriteria(BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build();
注意:退避延遲時(shí)間不精確,在兩次重試之間可能會(huì)有幾秒鐘的差異,但絕不會(huì)低于配置中指定的初始退避延遲時(shí)間。
最短退避延遲時(shí)間設(shè)置為允許的最小值,即 10 秒。由于政策為 LINEAR,每次嘗試重試時(shí),重試間隔都會(huì)增加約 10 秒。例如,第一次運(yùn)行以 Result.retry() 結(jié)束并在 10 秒后重試;然后,如果工作在后續(xù)嘗試后繼續(xù)返回 Result.retry(),那么接下來會(huì)在 20 秒、30 秒、40 秒后重試,以此類推。如果退避政策設(shè)置為 EXPONENTIAL,那么重試時(shí)長序列將接近 20、40、80 秒,以此類推。
你可以為WorkRequest添加tag,從而使得你可以通過WorkManager.getWorkInfosByTag(String)獲取WorkRequest的工作狀態(tài)WorkInfo,你也可以直接通過WorkManager.cancelAllWorkByTag(String)取消對(duì)應(yīng)標(biāo)記的所有WorkRequest.
標(biāo)記工作
由WorkRequest中關(guān)于tag的定義Set mTags可知,你可以為WorkRequest定義多個(gè)標(biāo)記,當(dāng)然的,你也可以為多個(gè)WorkRequest定義同一個(gè)標(biāo)記用以統(tǒng)一管理。
WorkRequest request =
new OneTimeWorkRequest.Builder(SimpleWorker.class)
.addTag("TAG")
.build();
// 根據(jù)tag取消任務(wù)
WorkManager.getInstance(this).cancelAllWorkByTag("TAG");
// 根據(jù)tag查找任務(wù)狀態(tài)
WorkManager.getInstance(this).getWorkInfosByTag("TAG");
取消工作
// by id
workManager.cancelWorkById(syncWorker.id);
// by name
workManager.cancelUniqueWork("sync");
// by tag
workManager.cancelAllWorkByTag("syncTag");
注意事項(xiàng)
這里我們總結(jié)一下使用WorkManager的一些小Tips。
PeriodicWorkRequest周期任務(wù)可以定義的最短重復(fù)間隔是 15 分鐘(與 JobScheduler API 相同)
延遲工作:執(zhí)行工作器的確切時(shí)間還取決于 WorkRequest 中使用的約束和系統(tǒng)優(yōu)化方式。WorkManager 經(jīng)過設(shè)計(jì),能夠在滿足這些約束的情況下提供可能的最佳行為。
退避延遲時(shí)間不精確,在兩次重試之間可能會(huì)有幾秒鐘的差異,但絕不會(huì)低于配置中指定的初始退避延遲時(shí)間。
cancelAllWorkByTag(String) 會(huì)取消具有給定標(biāo)記的所有工作。
WorkRequest保證一定執(zhí)行,但不保證一定在什么時(shí)間執(zhí)行。
WorkManager中Service組件之一的SystemAlarmService。
SystemAlarmService 主要邏輯:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if (mIsShutdown) {
Logger.get().info(TAG,
"Re-initializing SystemAlarmDispatcher after a request to shut-down.");
// Destroy the old dispatcher to complete it's lifecycle.
mDispatcher.onDestroy();
// Create a new dispatcher to setup a new lifecycle.
initializeDispatcher();
// Set mIsShutdown to false, to correctly accept new commands.
mIsShutdown = false;
}
if (intent != null) {
//添加調(diào)度事件,若當(dāng)前無任務(wù)則立即調(diào)度
mDispatcher.add(intent, startId);
}
// If the service were to crash, we want all unacknowledged Intents to get redelivered.
//如果服務(wù)崩潰,我們希望所有未確認(rèn)的意圖都得到重新交付。
return Service.START_REDELIVER_INTENT;
}
我們看到, 此服務(wù)在創(chuàng)建時(shí)初始化并綁定鬧鐘事件調(diào)度器,當(dāng)服務(wù)被start時(shí),若有intent事件,則將intent傳遞給調(diào)度器. 調(diào)度器收到intent后執(zhí)行以下操作:
@MainThread
public boolean add(@NonNull final Intent intent, final int startId) {
Logger.get().debug(TAG, String.format("Adding command %s (%s)", intent, startId));
assertMainThread();
String action = intent.getAction();
if (TextUtils.isEmpty(action)) {
Logger.get().warning(TAG, "Unknown command. Ignoring");
return false;
}
// If we have a constraints changed intent in the queue don't add a second one. We are
// treating this intent as special because every time a worker with constraints is complete
// it kicks off an update for constraint proxies.
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.
// 如果是第一個(gè)命令則執(zhí)行
// 一般情況下,若上一個(gè)命令執(zhí)行完畢會(huì)被移除
processCommand();
}
}
return true;
}
當(dāng)鬧鐘調(diào)度器收到新命令,會(huì)將新命令放入命令集合,若新命令為集合中第一個(gè)命令則直接進(jìn)入執(zhí)行命令邏輯中,否則不處理,前一命令執(zhí)行完畢后再次調(diào)用執(zhí)行命令:
@MainThread
private void processCommand() {
// do something
mWorkManager.getWorkTaskExecutor().executeOnBackgroundThread(new Runnable() {
// do something
try {
// 若當(dāng)前命令不為空,則將命令交給狀態(tài)機(jī)執(zhí)行
mCommandHandler.onHandleIntent(mCurrentIntent, startId, SystemAlarmDispatcher.this);
} catch (Throwable throwable) {}
finally {
// 當(dāng)前命令執(zhí)行完畢,回調(diào)SystemAlarmDispatcher處理命令集以及后續(xù)操作
postOnMainThread( new
(SystemAlarmDispatcher.this));
}
}
}
當(dāng)processCommand被調(diào)用,將開啟新的線程處理命令,主要干了兩件事:
將命令交給CommandHandler分發(fā)[規(guī)劃任務(wù)、重新規(guī)劃任務(wù)、約束條件改變、執(zhí)行完畢…]事件
將事件處理完畢回調(diào)到鬧鐘事件調(diào)度器,由調(diào)度器處理后續(xù)事宜。
我們來依次跟進(jìn)這兩件事:
@WorkerThread
void onHandleIntent(@NonNull Intent intent,int startId, @NonNull SystemAlarmDispatcher dispatcher) {
String action = intent.getAction();
if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
// 約束條件改變
handleConstraintsChanged(intent, startId, dispatcher);
} else if (ACTION_RESCHEDULE.equals(action)) {
// 異常中斷,需重新規(guī)劃
handleReschedule(intent, startId, dispatcher);
} else {
Bundle extras = intent.getExtras();
if (!hasKeys(extras, KEY_WORKSPEC_ID)) {
// log error
} else {
if (ACTION_SCHEDULE_WORK.equals(action)) {
// 規(guī)劃任務(wù)
handleScheduleWorkIntent(intent, startId, dispatcher);
} else if (ACTION_DELAY_MET.equals(action)) {
// 時(shí)延到點(diǎn)
handleDelayMet(intent, startId, dispatcher);
} else if (ACTION_STOP_WORK.equals(action)) {
// 取消任務(wù)
handleStopWork(intent, dispatcher);
} else if (ACTION_EXECUTION_COMPLETED.equals(action)) {
// 完成任務(wù)
handleExecutionCompleted(intent, startId);
} else {
// log error
}
}
}
}
我們看到這里主要依據(jù)命令(intent)的action執(zhí)行命令分發(fā)操作,此處我們主要關(guān)注handleDelayMet 時(shí)延結(jié)束后,處理任務(wù)的主線:
private void handleDelayMet(
@NonNull Intent intent,
int startId,
@NonNull SystemAlarmDispatcher dispatcher) {
Bundle extras = intent.getExtras();
// 保證任務(wù)處理線程安全
synchronized (mLock) {
String workSpecId = extras.getString(KEY_WORKSPEC_ID);
// Check to see if we are already handling an ACTION_DELAY_MET for the WorkSpec.
// If we are, then there is nothing for us to do.
// 檢查該任務(wù)是否已經(jīng)在即將執(zhí)行隊(duì)伍(map)中,如果是則不再重復(fù)操作
if (!mPendingDelayMet.containsKey(workSpecId)) {
DelayMetCommandHandler delayMetCommandHandler = new DelayMetCommandHandler(mContext, startId, workSpecId, dispatcher);
// 將任務(wù)放入即將執(zhí)行隊(duì)伍(map)中
mPendingDelayMet.put(workSpecId, delayMetCommandHandler);
// 處理任務(wù)
delayMetCommandHandler.handleProcessWork();
} else {
// log error
}
}
}
我們可以看到,CommandHandler中維護(hù)了一個(gè) mPendingDelayMet的Map來保證單次任務(wù)不被多次處理,若命令未被處理過,則將其加入到隊(duì)伍中,然后調(diào)用delayMetCommandHandler.handleProcessWork()去處理任務(wù):
@WorkerThread
void handleProcessWork() {
// 創(chuàng)建WakeLocks
mWakeLock = WakeLocks.newWakeLock(...);
// 獲取WorkSpec
WorkSpec workSpec = mDispatcher.getWorkManager().getWorkDatabase().workSpecDao().getWorkSpec(mWorkSpecId);
// 處理通常不會(huì)發(fā)生的情況 - 未獲取到WorkSpec。
// 取消工作應(yīng)該刪除alarm,但是如果alarm已經(jīng)觸發(fā),那么就觸發(fā)一個(gè)stop work請(qǐng)求來刪除掛起的delay met命令處理程序。
if (workSpec == null) {
stopWork();
return;
}
// 跟蹤工作任務(wù)是否有約束
mHasConstraints = workSpec.hasConstraints();
if (!mHasConstraints) {
// 若無約束
onAllConstraintsMet(Collections.singletonList(mWorkSpecId));
} else {
// 允許跟蹤器報(bào)告約束更改
// 此處可見,我們可以將時(shí)延視作一種特殊的約束
mWorkConstraintsTracker.replace(Collections.singletonList(workSpec));
}
}
這里判斷了任務(wù)狀態(tài)及約束狀態(tài),依據(jù)約束狀態(tài)執(zhí)行任務(wù)或更新任務(wù)狀態(tài),
執(zhí)行任務(wù)時(shí):
@Override
public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
// 保證任務(wù)正確
if (!workSpecIds.contains(mWorkSpecId)) {
return;
}
// 保證執(zhí)行安全
synchronized (mLock) {
if (mCurrentState == STATE_INITIAL) {
// 更改狀態(tài)
mCurrentState = STATE_START_REQUESTED;
// startWork => doWork
//這里沒有使用WorkManagerImpl#startWork(),因?yàn)槲覀冃枰捞幚砥魇欠裨谶@里將工作排隊(duì)
boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);
// 若任務(wù)狀態(tài)是輪詢
if (isEnqueued) {
// 設(shè)置計(jì)時(shí)器以強(qiáng)制已進(jìn)入隊(duì)列的任務(wù)配額
mDispatcher.getWorkTimer()
.startTimer(mWorkSpecId, WORK_PROCESSING_TIME_IN_MS, this);
} else {
// 執(zhí)行完畢或取消,則清除狀態(tài)
cleanUp();
}
} else {
Logger.get().debug(TAG, String.format("Already started work for %s", mWorkSpecId));
}
}
}
這里最終改變?nèi)蝿?wù)狀態(tài)并調(diào)用Processor.startWork()執(zhí)行任務(wù),我們將關(guān)注Processor.startWork(),這個(gè)方法最終執(zhí)行到我們定義的Worker中doWork()方法,并且前面說到的WorkManager中負(fù)責(zé)任務(wù)執(zhí)行的Service們最終都會(huì)調(diào)用到Processor.startWork()。
首先我們簡單過一下 mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras)的代碼:
public boolean startWork(@NonNull String id, @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
WorkerWrapper workWrapper;
// 保證任務(wù)隊(duì)列(MAP)只進(jìn)或只出
synchronized (mLock) {
// doing 驗(yàn)證id不在隊(duì)列中
// 初始化WorkerWrapper
workWrapper = new WorkerWrapper.Builder(...).build();
ListenableFuture<Boolean> future = workWrapper.getFuture();
// 為執(zhí)行結(jié)果添加監(jiān)聽,注意這里監(jiān)聽回調(diào)何時(shí)調(diào)用后面會(huì)說明
// 特別注意:future.addListener中 FutureListener 是一個(gè)Runnable對(duì)象
// 特別注意: FutureListener 是一個(gè)Runnable對(duì)象
future.addListener(
new FutureListener(this, id, future),
mWorkTaskExecutor.getMainThreadExecutor());
// 將此任務(wù)加入隊(duì)列
mEnqueuedWorkMap.put(id, workWrapper);
}
// 使用工作線程執(zhí)行任務(wù)
mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
}
mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras)主要做了三個(gè)操作:
- 將任務(wù)要素封裝為WorkerWrapper并為其執(zhí)行期望添加監(jiān)聽
- 任務(wù)要素入隊(duì)
- 工作線程執(zhí)行處理任務(wù)
相對(duì)主要的,我們繼續(xù)跟進(jìn)工作線程處理任務(wù)流程execute(Runnable), excute將WorkerWrapper繼續(xù)封裝為Task并加入任務(wù)隊(duì)列,若當(dāng)前無活躍Task則安排任務(wù)執(zhí)行,任務(wù)執(zhí)行完畢后繼續(xù)檢查任務(wù)隊(duì)列,直到任務(wù)隊(duì)列空置為止:
@Override
public void execute(@NonNull Runnable command) {
// 保證任務(wù)隊(duì)列只進(jìn)或只出
synchronized (mLock) {
// 封裝任務(wù)并入隊(duì)
mTasks.add(new Task(this, command));
// 若無活躍任務(wù),則嘗試安排任務(wù)
if (mActive == null) {
scheduleNext();
}
}
}
// Synthetic access
void scheduleNext() {
// 保證任務(wù)隊(duì)列只進(jìn)或只出
synchronized (mLock) {
// 有任務(wù)且任務(wù)出隊(duì)
if ((mActive = mTasks.poll()) != null) {
// 執(zhí)行任務(wù)
mExecutor.execute(mActive);
}
}
}
mExecutor.execute(mActive)會(huì)通過Task執(zhí)行execute(Runnable)傳入的Runnable,即執(zhí)行WorkerWrapper,Task不論WorkerWrapper執(zhí)行結(jié)果最終會(huì)繼續(xù)輪詢?nèi)蝿?wù)隊(duì)列。此時(shí),我們將目光移到WorkerWrapper.run():
@WorkerThread
@Override
public void run() {
mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
mWorkDescription = createWorkDescription(mTags);
runWorker();
}
private void runWorker() {
// 各個(gè)狀態(tài)判斷最終調(diào)用到resolve(false);
resolve(true); // OR resolve(false);
// 嘗試將工作設(shè)置為運(yùn)行狀態(tài)。注意,這可能會(huì)失敗,因?yàn)樽詮纳洗卧谶@個(gè)函數(shù)的頂部檢查之后,另一個(gè)線程可能已經(jīng)修改了數(shù)據(jù)庫。
if (trySetRunning()) {
if (tryCheckForInterruptionAndResolve()) {
return;
}
final SettableFuture<ListenableWorker.Result> future = SettableFuture.create();
// Call mWorker.startWork() on the main thread.
mWorkTaskExecutor.getMainThreadExecutor()
.execute(new Runnable() {
@Override
public void run() {
try {
// 調(diào)用Worker執(zhí)行工作任務(wù)
mInnerFuture = mWorker.startWork();
// 將任務(wù)結(jié)果回調(diào)添加到期望回調(diào)中
future.setFuture(mInnerFuture);
} catch (Throwable e) {
future.setException(e);
}
}
});
// Avoid synthetic accessors.
final String workDescription = mWorkDescription;
future.addListener(new Runnable() {
@Override
@SuppressLint("SyntheticAccessor")
public void run() {
// 最終調(diào)用到resolve(false);
resolve(false);
}
}, mWorkTaskExecutor.getBackgroundExecutor());
} else {
// 最終調(diào)用到resolve(false);
resolve(boolean);
}
}
private void resolve(final boolean needsReschedule) {
// 數(shù)據(jù)庫事務(wù)操作
mFuture.set(needsReschedule);
}
runWorker() 中包含各種任務(wù)狀態(tài)的判斷,并嘗試將任務(wù)置為運(yùn)行狀態(tài),若設(shè)置運(yùn)行狀態(tài)成功,則調(diào)用Worker.startWork() 即最終會(huì)走向個(gè)人定義的Worker的doWork()方法:
@Override
public final @NonNull ListenableFuture<Result> startWork() {
mFuture = SettableFuture.create();
getBackgroundExecutor().execute(new Runnable() {
@Override
public void run() {
try {
Result result = doWork();
mFuture.set(result);
} catch (Throwable throwable) {
mFuture.setException(throwable);
}
}
});
return mFuture;
}
DequeueAndCheckForCompletion如何完成回調(diào):
DequeueAndCheckForCompletion 是一個(gè)Runnable對(duì)象,postOnMainThread時(shí)調(diào)用run:
@Override
public void run() {
mDispatcher.dequeueAndCheckForCompletion();
}
最終回調(diào)到dequeueAndCheckForCompletion:
void dequeueAndCheckForCompletion() {
// 保證mIntents命令集只進(jìn)或只出
synchronized (mIntents) {
if (mCurrentIntent != null) {
// 從命令集中移除已完成(第一個(gè))命令
if (!mIntents.remove(0).equals(mCurrentIntent)) {
throw new IllegalStateException("something");
}
mCurrentIntent = null;
}
SerialExecutor serialExecutor = mTaskExecutor.getBackgroundExecutor();
// 若無預(yù)計(jì)執(zhí)行和待執(zhí)行命令,則通知SystemAlarmService執(zhí)行完畢
if (!mCommandHandler.hasPendingCommands()
&& mIntents.isEmpty()
&& !serialExecutor.hasPendingTasks()) {
if (mCompletedListener != null) {
mCompletedListener.onAllCommandsCompleted();
}
} else if (!mIntents.isEmpty()) {
// 若命令集中還有未執(zhí)行的命令,則繼續(xù)執(zhí)行
processCommand();
}
}
}
此時(shí),若當(dāng)前命令存在,則從命令集中移除當(dāng)前命令。若無預(yù)計(jì)執(zhí)行和待執(zhí)行命令,則調(diào)用mCompletedListener.onAllCommandsCompleted()通知SystemAlarmService執(zhí)行完畢;若命令集合中還有未執(zhí)行命令,則調(diào)用processCommand()繼續(xù)執(zhí)行。
而SystemAlarmService.onAllCommandsCompleted()最終會(huì)執(zhí)行stopSelf()停止服務(wù)
SystemJobService
Service invoked by {@link android.app.job.JobScheduler} to run work tasks.
服務(wù)將被JobScheduler調(diào)用來執(zhí)行工作任務(wù)。
從描述可得SystemJobService與SystemAlarmService職責(zé)是一致的,都是“run work tasks”。
類似地,我們可以猜測(cè)SystemJobService執(zhí)行流程始于onStartJob終于onStopJob。
SystemJobService.onCreate()獲取了WorkManagerImpl實(shí)例并將執(zhí)行監(jiān)聽綁定到(任務(wù))處理器Processor:
// 還記得WorkManagerImpl是哪里初始化的嗎?
mWorkManagerImpl = WorkManagerImpl.getInstance(getApplicationContext());
// 記住此處addExecutionListener
// 記住此處addExecutionListener
// 記住此處addExecutionListener
mWorkManagerImpl.getProcessor().addExecutionListener(this);
當(dāng)SystemJobService被調(diào)用時(shí)會(huì)執(zhí)行SystemJobService.onStartJob(),此時(shí)會(huì)將JobParameters以workId為Key存入工作Map中,并初始化WorkerParameters.RuntimeExtras, 最終調(diào)用mWorkManagerImpl.startWork(workSpecId, runtimeExtras)執(zhí)行任務(wù):
public boolean onStartJob(@NonNull JobParameters params) {
// WorkManagerImpl NonNull
String workSpecId = getWorkSpecIdFromJobParameters(params);
// workSpecId NonNull
// 保證mJobParameters 工作參數(shù)集只進(jìn)或只出
synchronized (mJobParameters) {
if (mJobParameters.containsKey(workSpecId)) {
// log
return false;
}
// 存入工作參數(shù)
mJobParameters.put(workSpecId, params);
}
// 初始化WorkerParameters.RuntimeExtras
// WorkManagerImpl執(zhí)行任務(wù)
mWorkManagerImpl.startWork(workSpecId, runtimeExtras);
}
WorkManagerImpl.startWork后續(xù)會(huì)執(zhí)行 mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras)。
由Processor.startWork()中代碼可知,流程末尾onExecuted中遍歷執(zhí)行的executionListener.onExecuted(workSpecId, needsReschedule)會(huì)調(diào)用SystemJobService.onExecuted():
@Override
public void onExecuted(@NonNull String workSpecId, boolean needsReschedule) {
Logger.get().debug(TAG, String.format("%s executed on JobScheduler", workSpecId));
JobParameters parameters;
synchronized (mJobParameters) {
parameters = mJobParameters.remove(workSpecId);
}
if (parameters != null) {
jobFinished(parameters, needsReschedule);
}
}
當(dāng)然,根據(jù)JobService的生命周期,我們可以認(rèn)定,最后會(huì)調(diào)用SystemJobService.onStopJob()
@Override
public boolean onStopJob(@NonNull JobParameters params) {
if (mWorkManagerImpl == null) {
return true;
}
String workSpecId = getWorkSpecIdFromJobParameters(params);
if (TextUtils.isEmpty(workSpecId)) {
return false;
}
// mJobParameters數(shù)據(jù)安全
synchronized (mJobParameters) {
// 移出任務(wù)參數(shù)隊(duì)列(Map)
mJobParameters.remove(workSpecId);
}
// 結(jié)束此任務(wù)
mWorkManagerImpl.stopWork(workSpecId);
return !mWorkManagerImpl.getProcessor().isCancelled(workSpecId);
}
SystemForegroundService
在看過前面SystemAlarmService & SystenJobService 后,我本以為
SystemForegroundService也如前者一般是處理任務(wù)執(zhí)行的,但簡單閱讀代碼后發(fā)現(xiàn)并非如此,盡管結(jié)構(gòu)上與前者類似,但其主要作用是給運(yùn)行任務(wù)的服務(wù)提權(quán),保證其在前臺(tái)執(zhí)行。
SystemAlarmService & SystenJobService相似,SystemForegroundService也是LifecycleService的子類,主要方法有:
@Override
public void onCreate() {
super.onCreate();
sForegroundService = this;
initializeDispatcher();
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
// 若正在被關(guān)閉,則
if (mIsShutdown) {
// 結(jié)束舊 Dispatcher 的生命周期
mDispatcher.onDestroy();
// 創(chuàng)建新的Dispatcher并設(shè)置新的生命周期
initializeDispatcher();
mIsShutdown = false;
}
if (intent != null) {
// 告訴Dispatcher 有活兒來了
mDispatcher.onStartCommand(intent);
}
// 如果服務(wù)崩潰,我們希望所有未確認(rèn)的意圖都得到重新交付。
return Service.START_REDELIVER_INTENT;
}
@MainThread
private void initializeDispatcher() {
mHandler = new Handler(Looper.getMainLooper());
mNotificationManager = (NotificationManager)
getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
mDispatcher = new SystemForegroundDispatcher(getApplicationContext());
mDispatcher.setCallback(this);
}
初始化時(shí)創(chuàng)建新的SystemForegroundDispatcher綁定并設(shè)置新的生命周期,當(dāng)收到指令被啟動(dòng)時(shí),調(diào)用SystemForegroundDispatcher.onStartCommand():
// SystemForegroundDispatcher#onStartCommand
void onStartCommand(@NonNull Intent intent) {
String action = intent.getAction();
if (ACTION_START_FOREGROUND.equals(action)) {
handleStartForeground(intent);
// 調(diào)用handleNotify(),它反過來調(diào)用start前臺(tái)()作為處理這個(gè)命令的一部分。這對(duì)一些原始設(shè)備制造商來說很重要。
handleNotify(intent);
} else if (ACTION_NOTIFY.equals(action)) {
handleNotify(intent);
} else if (ACTION_CANCEL_WORK.equals(action)) {
handleCancelWork(intent);
}
}
當(dāng)intent (此處intent可視作前面提到的SystemXXXService的意圖) 狀態(tài)處于啟動(dòng)、更新狀態(tài)時(shí)會(huì)調(diào)用handleNotify(intent),取消時(shí)會(huì)調(diào)用handleCancelWork(intent),
狀態(tài)為取消時(shí),則最終會(huì)調(diào)用mWorkManagerImpl.cancelWorkById(UUID.fromString(workSpecId))取消任務(wù)執(zhí)行。
我們?cè)賮砜纯磆andleNotify干了哪些事情吧:
// SystemForegroundDispatcher#handleNotify
@MainThread
private void handleNotify(@NonNull Intent intent) {
int notificationId = intent.getIntExtra(KEY_NOTIFICATION_ID, 0);
int notificationType = intent.getIntExtra(KEY_FOREGROUND_SERVICE_TYPE, 0);
String workSpecId = intent.getStringExtra(KEY_WORKSPEC_ID);
Notification notification = intent.getParcelableExtra(KEY_NOTIFICATION);
if (notification != null && mCallback != null) {
ForegroundInfo info = new ForegroundInfo(
notificationId, notification, notificationType);
// 緩存信息
mForegroundInfoById.put(workSpecId, info);
if (TextUtils.isEmpty(mCurrentForegroundWorkSpecId)) {
// 這是擁有前臺(tái)生命周期的當(dāng)前workSpecId.
mCurrentForegroundWorkSpecId = workSpecId;
mCallback.startForeground(notificationId, notificationType, notification);
} else {
// 更新通知
mCallback.notify(notificationId, notification);
// 如有必要,更新前臺(tái)的通知,使其成為當(dāng)前所有前臺(tái)服務(wù)類型的聯(lián)合。
if (notificationType != FOREGROUND_SERVICE_TYPE_NONE
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
int foregroundServiceType = FOREGROUND_SERVICE_TYPE_NONE;
for (Map.Entry<String, ForegroundInfo> entry : mForegroundInfoById.entrySet()) {
ForegroundInfo foregroundInfo = entry.getValue();
foregroundServiceType |= foregroundInfo.getForegroundServiceType();
}
//緩存中取出信息
ForegroundInfo currentInfo = mForegroundInfoById.get(mCurrentForegroundWorkSpecId);
if (currentInfo != null) {
mCallback.startForeground(
currentInfo.getNotificationId(),
foregroundServiceType,
currentInfo.getNotification()
);
}
}
}
}
}
若當(dāng)前服務(wù)intent是首個(gè),那么就啟動(dòng)SystemForegroundService.startForeground()做周期性遞歸,而SystemForegroundService.startForeground()實(shí)際上是一個(gè)空方法,類似于使用了Looper保證SystemForegroundService一直運(yùn)行:
// SystemForegroundService#startForeground
@Override
public void startForeground(
final int notificationId,
final int notificationType,
@NonNull final Notification notification) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(notificationId, notification, notificationType);
} else {
startForeground(notificationId, notification);
}
}
});
}
若當(dāng)前服務(wù)intent不是首個(gè),則更新一則通知,并合并前臺(tái)服務(wù)類型,mCallback.notify(notificationId, notification):
// SystemForegroundService#notify
@Override
public void notify(final int notificationId, @NonNull final Notification notification) {
mHandler.post(new Runnable() {
@Override
public void run() {
mNotificationManager.notify(notificationId, notification);
}
});
}
SystemForegroundService會(huì)在任務(wù)啟動(dòng)時(shí)啟動(dòng),且會(huì)創(chuàng)建一個(gè)類似于Looper的結(jié)構(gòu)保持服務(wù)前臺(tái)持續(xù)運(yùn)行,后續(xù)有任務(wù)觸發(fā)時(shí),會(huì)發(fā)送notification以保證其執(zhí)行任務(wù)的服務(wù)可被視作前臺(tái)服務(wù)保持運(yùn)行。
廣播接收者
WorkManager使用的重要組件:廣播接收者,主要涉及意外停止監(jiān)聽廣播ForceStopRunnable.BroadcastReceiver、約束狀態(tài)監(jiān)聽廣播ConstraintProxy.*、啟動(dòng)重新規(guī)劃服務(wù)的廣播RescheduleReceiver、代理約束更新廣播ConstraintProxyUpdateReceiver、測(cè)試診斷廣播DiagnosticsReceiver。此類組件為WorkManager穩(wěn)定運(yùn)行、重新規(guī)劃、約束更新提供了支持.
ForceStopRunnable
WorkManager is restarted after an app was force stopped. Alarms and Jobs get cancelled when an application is force-stopped. To reschedule, we create a pending alarm that will not survive force stops.
強(qiáng)制停止應(yīng)用程序后,WorkManager將重新啟動(dòng)。當(dāng)應(yīng)用被強(qiáng)制停止時(shí),Alarms和Jobs將被取消。為了重新安排,我們要?jiǎng)?chuàng)建一個(gè)無法通過強(qiáng)制停止的Alarm。
在介紹ForceStopRunnable.BroadcastReceiver前,我們先簡單看一下它的外部類ForceStopRunnable。
ForceStopRunnable是一個(gè)Runnable對(duì)象,核心方法是run, 其在WorkManager初始化時(shí)調(diào)用WorkManagerImpl.internalInit(), 構(gòu)造并調(diào)用了ForceStopRunnable.run():
@Override
public void run() {
// 如果需要,將數(shù)據(jù)庫遷移到不備份的目錄。
WorkDatabasePathHelper.migrateDatabase(mContext);
// 清除屬于WorkManager的無效作業(yè),以及由于應(yīng)用程序崩潰(運(yùn)行狀態(tài))而可能被中斷的作業(yè)。
try {
boolean needsScheduling = cleanUp();
if (shouldRescheduleWorkers()) {
mWorkManager.rescheduleEligibleWork();
// 將WorkManager標(biāo)記為已遷移。
mWorkManager.getPreferenceUtils().setNeedsReschedule(false);
} else if (isForceStopped()) {
// 異常退出,重新規(guī)劃任務(wù)
mWorkManager.rescheduleEligibleWork();
} else if (needsScheduling) {
// 發(fā)現(xiàn)未完成任務(wù),規(guī)劃它們
Schedulers.schedule(
mWorkManager.getConfiguration(),
mWorkManager.getWorkDatabase(),
mWorkManager.getSchedulers());
}
mWorkManager.onForceStopRunnableCompleted();
} catch (SQLiteCantOpenDatabaseException
| SQLiteDatabaseCorruptException
| SQLiteAccessPermException exception) {
// ForceStopRunnable通常是訪問數(shù)據(jù)庫(或應(yīng)用程序的內(nèi)部數(shù)據(jù)目錄)的第一件事。
// 這意味著奇怪的PackageManager錯(cuò)誤被歸咎于ForceStopRunnable,這是不幸的。
// 這為開發(fā)人員提供了更好的錯(cuò)誤消息。
String message =
"The file system on the device is in a bad state. WorkManager cannot access "
+ "the app's internal data store.";
Logger.get().error(TAG, message, exception);
throw new IllegalStateException(message, exception);
}
}
我們發(fā)現(xiàn)ForceStopRunable主要職責(zé)是處理異常中斷后對(duì)WorkManager中任務(wù)進(jìn)行清理和重新規(guī)劃,run()主要干了3件事:
如果需要,將數(shù)據(jù)庫遷移到不備份的目錄
取消無效的JobScheduler作業(yè)/重新調(diào)度以前正在運(yùn)行的作業(yè)
針對(duì)異常中斷時(shí)WorkManager的狀態(tài)進(jìn)行不同的調(diào)度操作
那么他是如何判斷是否是強(qiáng)制中斷的呢,我們可以看下強(qiáng)制中斷判斷代碼isForceStopped():
@VisibleForTesting
public boolean isForceStopped() {
// 當(dāng)應(yīng)用程序在Eclair MR1強(qiáng)制停止啟動(dòng)時(shí),Alarm被取消。
// 在N-MR1中引入了強(qiáng)制停止作業(yè)的取消(SDK 25)。
// 盡管API 23、24可能是安全的,但oem可能會(huì)選擇采用不同的方法。
PendingIntent pendingIntent = getPendingIntent(mContext, FLAG_NO_CREATE);
if (pendingIntent == null) {
setAlarm(mContext);
return true;
} else {
return false;
}
}
通過判斷鬧鐘廣播是否存在來確定應(yīng)用是否被強(qiáng)行停止,若不存在即鬧鐘Alarm被取消即是強(qiáng)制中斷,此時(shí)將重新設(shè)置一個(gè)鬧鐘Alarm;
讓我們來看看上文中的ForceStopRunnable.BroadcastReceiver鬧鐘廣播。
BroadcastReceiver
A {@link android.content.BroadcastReceiver} which takes care of recreating the long lived alarm which helps track force stops for an application. This is the target of the alarm set by ForceStopRunnable in {@link #setAlarm(Context)}. 一個(gè){@link android.content.BroadcastReceiver}負(fù)責(zé)重新創(chuàng)建長壽命的Alarm,這有助于跟蹤應(yīng)用程序的強(qiáng)制停止。這是在{@link #setAlarm(Context)}中由forceoprunnable設(shè)置的Alarm目標(biāo)。
從描述可知,此廣播的主要目的是負(fù)責(zé)創(chuàng)建長期存活的鬧鐘,用以追蹤應(yīng)用程序的強(qiáng)制停止。
其代碼相對(duì)簡單,若收到action為ACTION_FORCE_STOP_RESCHEDULE的廣播,則設(shè)置一個(gè)長達(dá)十年的可喚醒設(shè)備的鬧鐘,然后再來一次(發(fā)送action為ACTION_FORCE_STOP_RESCHEDULE的廣播)。
// ForceStopRunnable
private static PendingIntent getPendingIntent(Context context, int flags) {
Intent intent = getIntent(context);
// 發(fā)送廣播的PendingIntent
return PendingIntent.getBroadcast(context, ALARM_ID, intent, flags);
}
@VisibleForTesting
static Intent getIntent(Context context) {
Intent intent = new Intent();
intent.setComponent(new ComponentName(context, ForceStopRunnable.BroadcastReceiver.class));
// 廣播intent
intent.setAction(ACTION_FORCE_STOP_RESCHEDULE);
return intent;
}
static void setAlarm(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
//使用FLAG_UPDATE_CURRENT,因?yàn)槲覀冎恍枰@個(gè)Alarm的一次實(shí)例
PendingIntent pendingIntent = getPendingIntent(context, FLAG_UPDATE_CURRENT);
// 設(shè)置執(zhí)行時(shí)間為十年
long triggerAt = System.currentTimeMillis() + TEN_YEARS;
if (alarmManager != null) {
if (Build.VERSION.SDK_INT >= 19) {
// RTC_WAKEUP:絕對(duì)時(shí)間可喚醒類型,十年后,干啥
alarmManager.setExact(RTC_WAKEUP, triggerAt, pendingIntent);
} else {
alarmManager.set(RTC_WAKEUP, triggerAt, pendingIntent);
}
}
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static class BroadcastReceiver extends android.content.BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// Our alarm somehow got triggered, so make sure we reschedule it. This should really never happen because we set it so far in the future.
// 我們的鬧鐘不知怎么被觸發(fā)了,所以一定要重新安排時(shí)間。這是不應(yīng)該發(fā)生的,因?yàn)槲覀儗⑺O(shè)置在遙遠(yuǎn)的未來(嘿,十年后再見)。
if (intent != null) {
String action = intent.getAction();
if (ACTION_FORCE_STOP_RESCHEDULE.equals(action)) {
Logger.get().verbose(
TAG,
"Rescheduling alarm that keeps track of force-stops.");
ForceStopRunnable.setAlarm(context);
}
}
}
}
有前文可知,應(yīng)用啟動(dòng)時(shí)會(huì)初始化WorkManager,WorkManager初始化則會(huì)執(zhí)行ForceStopRunnable.run(), 此時(shí)一般會(huì)調(diào)用setAlarm(), 創(chuàng)建Alarm,極端情況下會(huì)存在應(yīng)用存活十年的情況,此BroadcastReceiver即是用于處理這種情況的,當(dāng)十年后又收到了這個(gè)廣播,那么我們?cè)趧?chuàng)建一個(gè)十年期的鬧鐘Alarm.
ConstraintProxy
BatteryNotLowProxy
BatteryChargingProxy
StorageNotLowProxy
NetworkStateProxy
上述BatteryNotLowProxy、BatteryChargingProxy、StorageNotLowProxy、NetworkStateProxy均是ConstraintProxy的子類,實(shí)現(xiàn)均一致,僅有注冊(cè)action不同,用以針對(duì)不同action的系統(tǒng)廣播更新約束狀態(tài), 此處一并分析之。
以NetworkStateProxy為例,當(dāng)網(wǎng)絡(luò)狀態(tài)變化,應(yīng)用收到"android.net.conn.CONNECTIVITY_CHANGE"廣播,觸發(fā)NetworkStateProxy.onReceive():
@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);
}
調(diào)用CommandHandler.createConstraintsChangedIntent(context)調(diào)起SystemAlarmService, 其intent的action為"ACTION_CONSTRAINTS_CHANGED":
static Intent createConstraintsChangedIntent(@NonNull Context context) {
Intent intent = new Intent(context, SystemAlarmService.class);
intent.setAction(ACTION_CONSTRAINTS_CHANGED);
return intent;
}
最終會(huì)調(diào)用到CommandHandler.onHandleIntent():
void onHandleIntent(@NonNull Intent intent, int startId, @NonNull SystemAlarmDispatcher dispatcher) {
String action = intent.getAction();
if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
handleConstraintsChanged(intent, startId, dispatcher);
}
// 其他
}
private void handleConstraintsChanged(
@NonNull Intent intent, int startId,
@NonNull SystemAlarmDispatcher dispatcher) {
ConstraintsCommandHandler changedCommandHandler =
new ConstraintsCommandHandler(mContext, startId, dispatcher);
changedCommandHandler.handleConstraintsChanged();
}
此時(shí)intent的action為"ACTION_CONSTRAINTS_CHANGED",最終會(huì)調(diào)用到ConstraintsCommandHandler.handleConstraintsChanged():
void handleConstraintsChanged() {
List<WorkSpec> candidates = mDispatcher.getWorkManager().getWorkDatabase()
.workSpecDao()
.getScheduledWork();
//更新約束代理以潛在地禁用先前完成的WorkSpecs的代理。
ConstraintProxy.updateAll(mContext, candidates);
// 這需要在每個(gè)約束控制器中填充匹配的WorkSpec id。標(biāo)記正在更新這些工作的狀態(tài)
mWorkConstraintsTracker.replace(candidates);
List<WorkSpec> eligibleWorkSpecs = new ArrayList<>(candidates.size());
// 篩選候選人應(yīng)該已經(jīng)規(guī)劃好了。
long now = System.currentTimeMillis();
for (WorkSpec workSpec : candidates) {
String workSpecId = workSpec.id;
long triggerAt = workSpec.calculateNextRunTime();
// 時(shí)間條件符合且無約束或約束已符合,則加入符合條件集合
if (now >= triggerAt && (!workSpec.hasConstraints()
|| mWorkConstraintsTracker.areAllConstraintsMet(workSpecId))) {
eligibleWorkSpecs.add(workSpec);
}
}
for (WorkSpec workSpec : eligibleWorkSpecs) {
String workSpecId = workSpec.id;
Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId);
// 告訴SystemAlarmDispatcher這個(gè)工作已經(jīng)符合條件了,請(qǐng)給安排上
mDispatcher.postOnMainThread(
new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));
}
// 重置正在更新這些工作狀態(tài)的標(biāo)記
mWorkConstraintsTracker.reset();
}
此方法先獲取了規(guī)劃中的工作生成集合,調(diào)用了ConstraintProxy.updateAll()方法更新約束代理以潛在地禁用先前完成的WorkSpecs的代理,遍歷判斷每個(gè)WorkSpec是否符合條件,遍歷符合約束條件的列表,告訴SystemAlarmDispatcher添加任務(wù),若SystemAlarmDispatcher中無任務(wù)則分發(fā)執(zhí)行任務(wù)(還記得processCommand()是一個(gè)環(huán)狀的輪詢嗎?)。
SystemAlarmDispatcher.AddRunnable()后續(xù)調(diào)用SystemAlarmDispatcher.add()在SystemAlarmService中也有
SystemAlarmDispatcher.AddRunnable() -> SystemAlarmDispatcher.add():
// SystemAlarmDispatcher
public boolean add(@NonNull final Intent intent, final int startId) {
String action = intent.getAction();
if (TextUtils.isEmpty(action)) {
return false;
}
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) {
processCommand();
}
}
return true;
}
static class AddRunnable implements Runnable {
@Override
public void run() {
mDispatcher.add(mIntent, mStartId);
}
}
ConstraintProxyUpdateReceiver
上面講到ConstraintsCommandHandler.handleConstraintsChanged()第一步會(huì)調(diào)用ConstraintProxy.updateAll(mContext, candidates)最終會(huì)發(fā)送廣播給ConstraintProxyUpdateReceiver更新約束代理以潛在地禁用先前完成的WorkSpecs的代理,我們簡單過一下。
static void updateAll(Context context, List<WorkSpec> workSpecs) {
boolean batteryNotLowProxyEnabled = false;
boolean batteryChargingProxyEnabled = false;
boolean storageNotLowProxyEnabled = false;
boolean networkStateProxyEnabled = false;
// 查找每個(gè)WorkSpec(是否有)約束條件狀態(tài)
for (WorkSpec workSpec : workSpecs) {
Constraints constraints = workSpec.constraints;
batteryNotLowProxyEnabled |= constraints.requiresBatteryNotLow();
batteryChargingProxyEnabled |= constraints.requiresCharging();
storageNotLowProxyEnabled |= constraints.requiresStorageNotLow();
networkStateProxyEnabled |= constraints.getRequiredNetworkType() != NOT_REQUIRED;
// 知道需要全部廣播接收者,就不用判斷了
if (batteryNotLowProxyEnabled && batteryChargingProxyEnabled
&& storageNotLowProxyEnabled && networkStateProxyEnabled) {
break;
}
}
Intent updateProxyIntent =
ConstraintProxyUpdateReceiver.newConstraintProxyUpdateIntent(
context,
batteryNotLowProxyEnabled,
batteryChargingProxyEnabled,
storageNotLowProxyEnabled,
networkStateProxyEnabled);
// ConstraintProxies are being updated via a separate broadcast receiver.
// For more information on why we do this look at b/73549299
context.sendBroadcast(updateProxyIntent);
}
這里先是判斷需要哪些廣播接收者,會(huì)發(fā)送廣播給ConstraintProxyUpdateReceiver,最終會(huì)調(diào)用:
@Override
public void onReceive(@NonNull final Context context, @Nullable final Intent intent) {
String action = intent != null ? intent.getAction() : null;
if (!ACTION.equals(action)) {
} else {
final PendingResult pendingResult = goAsync();
WorkManagerImpl workManager = WorkManagerImpl.getInstance(context);
TaskExecutor taskExecutor = workManager.getWorkTaskExecutor();
taskExecutor.executeOnBackgroundThread(new Runnable() {
@Override
public void run() {
try {
// 在后臺(tái)線程上執(zhí)行此操作,因?yàn)槭褂肞ackageManager來啟用或禁用代理涉及到對(duì)文件系統(tǒng)的寫操作。
boolean batteryNotLowProxyEnabled = intent.getBooleanExtra(
KEY_BATTERY_NOT_LOW_PROXY_ENABLED, false);
boolean batteryChargingProxyEnabled = intent.getBooleanExtra(
KEY_BATTERY_CHARGING_PROXY_ENABLED, false);
boolean storageNotLowProxyEnabled = intent.getBooleanExtra(
KEY_STORAGE_NOT_LOW_PROXY_ENABLED, false);
boolean networkStateProxyEnabled = intent.getBooleanExtra(
KEY_NETWORK_STATE_PROXY_ENABLED, false);
Logger.get().debug(
TAG,
String.format("Updating proxies: BatteryNotLowProxy enabled (%s), "
+ "BatteryChargingProxy enabled (%s), "
+ "StorageNotLowProxy (%s), "
+ "NetworkStateProxy enabled (%s)",
batteryNotLowProxyEnabled,
batteryChargingProxyEnabled,
storageNotLowProxyEnabled,
networkStateProxyEnabled));
PackageManagerHelper.setComponentEnabled(context, BatteryNotLowProxy.class,
batteryNotLowProxyEnabled);
PackageManagerHelper.setComponentEnabled(context,
BatteryChargingProxy.class,
batteryChargingProxyEnabled);
PackageManagerHelper.setComponentEnabled(context, StorageNotLowProxy.class,
storageNotLowProxyEnabled);
PackageManagerHelper.setComponentEnabled(context, NetworkStateProxy.class,
networkStateProxyEnabled);
} finally {
pendingResult.finish();
}
}
});
}
}
主要執(zhí)行了獲取前文提供的需要哪些約束廣播接收器,調(diào)用PackageManagerHelper.setComponentEnabled(context, StorageNotLowProxy.class, storageNotLowProxyEnabled)更改廣播接收者注冊(cè)信息:
/**
* 使用{@link PackageManager} 去開/關(guān)manifest聲明的組件
*
* @param context {@link Context}
* @param klazz The class of component
* @param enabled {@code true} 開關(guān)組件
*/
public static void setComponentEnabled(
@NonNull Context context,
@NonNull Class<?> klazz,
boolean enabled) {
try {
PackageManager packageManager = context.getPackageManager();
ComponentName componentName = new ComponentName(context, klazz.getName());
packageManager.setComponentEnabledSetting(componentName,
enabled
? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
} catch (Exception exception) {
}
}
RescheduleReceiver
Reschedules alarms on BOOT_COMPLETED and other similar scenarios.
在BOOT_COMPLETED和其他類似場(chǎng)景下重新調(diào)度Alarm。
DiagnosticsReceiver
The {@link android.content.BroadcastReceiver} which dumps out useful diagnostics information. 輸出有用的診斷信息