定義 Worker
和 WorkRequest
後,最後一步就是將工作加入佇列。要將工作加入佇列,最簡單方法就是呼叫 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 自動產生。不同於標記,不重複名稱只會與單一工作例項相關聯。
不重複工作可套用至一次性和週期性工作。取決於您要安排週期性或一次性工作,您可以呼叫下列其中一種方法,建立不重複工作序列。
- 適用於一次性工作的
WorkManager.enqueueUniqueWork()
- 適用於週期性工作的
WorkManager.enqueueUniquePeriodicWork()
這兩種方法都接受 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
:將新工作附加到現有工作的結尾。這項政策會導致新工作鏈結到現有的工作,並在現有工作結束後執行。
現有工作會成為新工作的「先決條件」。如果現有工作變成 CANCELLED
或 FAILED
,則新工作也會變為 CANCELLED
或 FAILED
。如果您希望新工作不受現有工作的狀態影響而一律執行,請改用 APPEND_OR_REPLACE
。
APPEND_OR_REPLACE
與APPEND
的功能類似,差別在於前者不受先決條件工作狀態的影響。如果現有工作是CANCELLED
或FAILED
,新工作仍會執行。
針對週期性工作,您必須提供支援以下 2 個選項的 ExistingPeriodicWorkPolicy
:REPLACE
和 KEEP
。這些選項的運作方式與 ExistingWorkPolicy 相同。
觀察您的工作
將工作加入佇列後,您隨時可以透過 name
、id
或與該工作相關的 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 支援透過標記、狀態和不重複工作名稱的組合來查詢工作。
以下範例會說明如何找到包含狀態為 FAILED
或 CANCELLED
的「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()
。
取消及停止工作
如果不再需要執行已排入佇列的工作,您可要求取消工作。要取消工作,可以使用 name
、id
或與其相關聯的 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)
取消)。 - 就不重複工作來說,您明確透過
REPLACE
的ExistingWorkPolicy
,將新的WorkRequest
加入佇列。系統會立即將舊的WorkRequest
視為取消, - 也不會再滿足您的工作限制條件。
- 系統因故指示應用程式停止工作。如果超過 10 分鐘的執行期限,就可能發生這種情況。這項工作會安排於稍後重試。
在下列情況下,工作站將會停止運作。
您應配合取消所有進行中的工作,並釋出工作站所占用的任何資源。舉例來說,您目前應關閉資料庫和檔案的開放式控制代碼。有兩種機制可讓您瞭解工作站何時停止運作。
onStopped() 回呼
工作站停止後,WorkManager 就會叫用 ListenableWorker.onStopped()
。請覆寫這個方法,以關閉您可能占用的任何資源。
isStopped() 屬性
您可以呼叫 ListenableWorker.isStopped()
方法來檢查工作站是否已停止。如果您在工作站中執行長時間或重複的作業,請務必經常查看這個屬性,並將該屬性視為盡快停止工作的信號。
注意:WorkManager 會忽略由收到 onStop 信號的工作站所設定的 Result
,因為系統已將該工作站視為停止。