WorkManager

一 使用入門
我們先了解WorkManager工作的流程:

  1. 誰來做? 定義一個(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)圖]

avatar

對(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)圖


avatar

周期性工作的初始狀態(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)記、處于 FAILEDCANCELLED 狀態(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. 輸出有用的診斷信息

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本篇文章完全轉(zhuǎn)載于微笑的江豚 的博客地址: https://my.oschina.net/JiangTun如有問...
    PeterHe888閱讀 2,042評(píng)論 0 2
  • 1、概述 在 I / O '18中,Google發(fā)布了Android Jetpack。它是一組庫,工具和架構(gòu)指南,...
    高丕基閱讀 7,682評(píng)論 1 12
  • WorkManager[https://developer.android.google.cn/topic/lib...
    谷歌開發(fā)者閱讀 3,648評(píng)論 1 18
  • WorkManager架構(gòu)組件是用來管理后臺(tái)工作任務(wù)。這個(gè)時(shí)候你可能會(huì)奇怪了Android不是已經(jīng) 有很多管...
    tuacy閱讀 6,650評(píng)論 4 14
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭,有人歡樂有人憂愁,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,834評(píng)論 28 54

友情鏈接更多精彩內(nèi)容