管理工作

定義 WorkerWorkRequest 後,最後一步就是將工作加入佇列。要將工作加入佇列,最簡單方法就是呼叫 WorkManager enqueue() 方法,並傳遞您要執行的 WorkRequest

Kotlin


val myWork: WorkRequest = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork)

Java


WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);

因此,建議您在將工作排入佇列時謹慎小心,以免重複。 例如,應用程式可能會嘗試每 24 小時將記錄上傳至後端服務。如果不留意,即便該項工作只需要執行一次,您有可能將相同的工作多次排入佇列。 為達到這項目標,您可以在安排工作時將其設為不重複工作

不重複工作

「不重複的工作」是強大有效的概念,可確保每個執行個體只能有一個特定的「名稱」。與 ID 不同的是,不重複名稱方便使用者辨識而且是由開發人員指定,而不是由 WorkManager 自動產生。不同於標記,不重複名稱只會與單一工作例項相關聯。

不重複工作可套用至一次性和週期性工作。取決於您要安排週期性或一次性工作,您可以呼叫下列其中一種方法,建立不重複工作序列。

這兩種方法都接受 3 個引數:

  • uniqueWorkName - 用來識別工作要求的 String
  • existingWorkPolicy - 如果已有該專屬名稱的相關作業尚未完成,enum 會指示 WorkManager 處置方式。詳情請參閱衝突解決政策
  • work - 要安排的 WorkRequest

我們可以透過不重複工作,解決先前提到的重複安排問題。

Kotlin


val sendLogsWorkRequest =
       PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
           .setConstraints(Constraints.Builder()
               .setRequiresCharging(true)
               .build()
            )
           .build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
           "sendLogs",
           ExistingPeriodicWorkPolicy.KEEP,
           sendLogsWorkRequest
)

Java


PeriodicWorkRequest sendLogsWorkRequest = new
      PeriodicWorkRequest.Builder(SendLogsWorker.class, 24, TimeUnit.HOURS)
              .setConstraints(new Constraints.Builder()
              .setRequiresCharging(true)
          .build()
      )
     .build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
     "sendLogs",
     ExistingPeriodicWorkPolicy.KEEP,
     sendLogsWorkRequest);

現在,如果 sendLogs 工作已在佇列中,而程式碼同時執行,系統仍會保留現有工作,不會新增任何工作。

如果您需要逐步建構較長的工作鏈,不重複工作序列也相當實用。例如,相片編輯應用程式可能會讓使用者復原很長的動作。每項復原作業可能需要一些時間才能完成,但必須以正確的順序執行。在這種情況下,應用程式可以建立「復原」鏈結,並視需要將每項復原作業附加到鏈結中。詳情請參閱將工作鏈結在一起一文。

衝突解決政策

安排不重複工作時,您必須告知 WorkManager 在發生衝突時應採取的動作。如要這麼做,請在將工作加入佇列時傳遞列舉值。

針對一次性工作,您必須提供支援 4 個處理衝突選項的 ExistingWorkPolicy

  • REPLACE:以新工作取代現有工作。這個選項會取消現有工作。
  • KEEP:保留現有工作並忽略新工作。
  • APPEND:將新工作附加到現有工作的結尾。這項政策會導致新工作鏈結到現有的工作,並在現有工作結束後執行。

現有工作會成為新工作的「先決條件」。如果現有工作變成 CANCELLEDFAILED,則新工作也會變為 CANCELLEDFAILED。如果您希望新工作不受現有工作的狀態影響而一律執行,請改用 APPEND_OR_REPLACE

  • APPEND_OR_REPLACEAPPEND 的功能類似,差別在於前者不受先決條件工作狀態的影響。如果現有工作是 CANCELLEDFAILED,新工作仍會執行。

針對週期性工作,您必須提供支援以下 2 個選項的 ExistingPeriodicWorkPolicyREPLACEKEEP。這些選項的運作方式與 ExistingWorkPolicy 相同。

觀察您的工作

將工作加入佇列後,您隨時可以透過 nameid 或與該工作相關的 tag 查詢 WorkManager,以檢查工作狀態。

Kotlin


// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>

Java


// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>

查詢會傳回 WorkInfo 物件的 ListenableFuture,包括工作的 id、標記、目前的 State,以及透過 Result.success(outputData) 設定的輸出資料。

每一種方法的 LiveData 變化版本都可讓您註冊事件監聽器,藉此觀察 WorkInfo 的變更。舉例來說,如果您想要在部分工作順利完成後向使用者顯示訊息,可以按照下列方式設定:

Kotlin


workManager.getWorkInfoByIdLiveData(syncWorker.id)
               .observe(viewLifecycleOwner) { workInfo ->
   if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
       Snackbar.make(requireView(),
      R.string.work_completed, Snackbar.LENGTH_SHORT)
           .show()
   }
}

Java


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();
   }
});

複雜的工作查詢

WorkManager 2.4.0 以上版本支援使用 WorkQuery 物件,對佇列工作執行複雜的查詢。WorkQuery 支援透過標記、狀態和不重複工作名稱的組合來查詢工作。

以下範例會說明如何找到包含狀態為 FAILEDCANCELLED 的「syncTag」標記,且不重複工作名稱為「preProcess」或「sync」的所有工作。

Kotlin


val workQuery = WorkQuery.Builder
       .fromTags(listOf("syncTag"))
       .addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
       .addUniqueWorkNames(listOf("preProcess", "sync")
    )
   .build()

val workInfos: ListenableFuture<List<WorkInfo>> = workManager.getWorkInfos(workQuery)

Java


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

WorkQuery 中的每個元件 (標記、狀態或名稱) 與其他元件都具有 AND 邏輯關係。而元件中的每個值都具有 OR 邏輯關係,例如:(name1 OR name2 OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...)

WorkQuery 也適用於 LiveData 對等項目 getWorkInfosLiveData()

取消及停止工作

如果不再需要執行已排入佇列的工作,您可要求取消工作。要取消工作,可以使用 nameid 或與其相關聯的 tag

Kotlin


// by id
workManager.cancelWorkById(syncWorker.id)

// by name
workManager.cancelUniqueWork("sync")

// by tag
workManager.cancelAllWorkByTag("syncTag")

Java


// by id
workManager.cancelWorkById(syncWorker.id);

// by name
workManager.cancelUniqueWork("sync");

// by tag
workManager.cancelAllWorkByTag("syncTag");

實際上,WorkManager 會檢查工作的 State。如果工作已經完成,則不會有任何異動。反之,工作狀態會變更為 CANCELLED,且日後也不會執行該工作。取決於這項工作的所有 WorkRequest 工作也都會變為 CANCELLED

目前 RUNNING 工作會收到對 ListenableWorker.onStopped() 的呼叫。覆寫這個方法即可處理任何潛在的清理作業。詳情請參閱停止執行中的工作站

停止執行中的工作站

以下幾個不同的原因可能會導致 WorkManager 停止執行 Worker

  • 您明確要求取消工作 (例如,透過呼叫 WorkManager.cancelWorkById(UUID) 取消)。
  • 不重複工作來說,您明確透過 REPLACEExistingWorkPolicy,將新的 WorkRequest 加入佇列。系統會立即將舊的 WorkRequest 視為取消,
  • 也不會再滿足您的工作限制條件。
  • 系統因故指示應用程式停止工作。如果超過 10 分鐘的執行期限,就可能發生這種情況。這項工作會安排於稍後重試。

在下列情況下,工作站將會停止運作。

您應配合取消所有進行中的工作,並釋出工作站所占用的任何資源。舉例來說,您目前應關閉資料庫和檔案的開放式控制代碼。有兩種機制可讓您瞭解工作站何時停止運作。

onStopped() 回呼

工作站停止後,WorkManager 就會叫用 ListenableWorker.onStopped()。請覆寫這個方法,以關閉您可能占用的任何資源。

isStopped() 屬性

您可以呼叫 ListenableWorker.isStopped() 方法來檢查工作站是否已停止。如果您在工作站中執行長時間或重複的作業,請務必經常查看這個屬性,並將該屬性視為盡快停止工作的信號。

注意:WorkManager 會忽略由收到 onStop 信號的工作站所設定的 Result,因為系統已將該工作站視為停止。