Android WorkManager官方指南
文章目錄
- 1. 使用WorkManager排程任務
- 1.1 主題
- 1.2 [WorkManager高階功能](https://developer.android.google.cn/topic/libraries/architecture/workmanager/advanced.html)
- 1.3 附加資源
- 2. WorkManager基礎
- 3. WorkManager高階主題
1. 使用WorkManager排程任務
WorkManager API可以方便地指定可延遲的非同步任務以及何時執行它們。這些API允許您建立任務並將其交給WorkManager立即執行或在適當的時機執行。
WorkManager根據裝置API級別和應用程式狀態等因素選擇適當的方法來執行您的任務。如果WorkManager在應用程式執行時執行您的任務之一,那麼WorkManager可以在應用程式的程序中的新執行緒中執行您的任務。如果您的應用程式沒有執行,WorkManager會選擇適當的方式來排程後臺任務——這取決於裝置API級別和包含的依賴,WorkManager可以使用
注意:WorkManager用於需要保證即使應用程式退出系統也會執行它們的任務,比如將應用程式資料上傳到伺服器。它不是用於程序內後臺工作,如果應用程式程序消失了,可以安全地終止;對於這樣的情況,我們建議使用執行緒池。
1.1 主題
使用WorkManager在您選擇的環境下排程單個任務,或者以指定的間隔重複執行任務。
1.2 WorkManager高階功能
設定鏈式任務序列,設定傳遞和返回值的任務,並設定命名的、獨特的工作序列。
1.3 附加資源
WorkManager是Android Jetpack架構元件。在Sunflower演示應用程式中使用它。
2. WorkManager基礎
使用WorkManager,您可以輕鬆地設定一個任務並將其交給系統,該任務可以在您指定的條件下執行。
這個概述涵蓋了最基本的WorkManager特性。在這個頁面中,您將學習如何設定任務,指定它應該執行的條件,並將其交給系統。您還將學習如何設定重複作業。
有關更高階的WorkManager特性的資訊,如作業連結以及傳遞和返回值,請參閱WorkManager高階特性。還有更多可用的特性;對於詳細資訊,請參閱WorkManager參考文件。
注意:要將WorkManager庫匯入到Android專案中,請參見將元件新增到專案中。
2.1 類與概念
WorkManager API使用了幾個不同的類。在某些情況下,您需要對API類中的一個子類進行分類。
這些是最重要的WorkManager類:
Worker:指定需要執行的任務。WorkManager APIs包括抽象Worker類。您繼承此類並在此處執行工作。
WorkRequest:表示一個單獨的任務。至少,WorkRequest物件指定哪個Worker類應該執行任務。但是,您還可以向WorkRequest物件新增細節,指定任務應該在哪些環境下執行。每個WorkRequest都有一個自動生成的惟一ID;可以使用該ID執行諸如取消佇列中的任務或獲取任務的狀態之類的操作。WorkRequest是一個抽象類;在程式碼中,您將使用某個子類,OneTimeWorkRequest或PeriodicWorkRequest。
WorkRequest.Builder:建立WorkRequest物件的幫助類。再次,您將使用一個子類,OneTimeWorkRequest.Builder或PeriodicWorkRequest.Builder。
Constraints:指定限制任務什麼時候執行(例如,“僅當裝置連線到網路時”)。你通過建立Constraints.Builder建立 Constraints物件。在建立WorkRequest之前,傳遞Constraints 物件給WorkRequest.Builder。
WorkManager:對工作請求進行排隊和管理。您將WorkRequest物件傳遞給WorkManager來執行任務。WorkManager以分散系統資源負載的方式排程任務,同時遵守您指定的約束。
WorkStatus:包含有關特定任務的資訊。WorkManager為每個WorkRequest物件提供LiveData。LiveData儲存一個WorkStatus物件;通過觀察LiveData,您可以確定當前任務的狀態,並在任務完成後獲得到返回值。
2.2 典型工作流
假設你正在編寫一個photo library app,並且該應用程式需要週期性地壓縮其儲存的影象。您希望使用WorkMeaveAPI來安排影象壓縮。在這種情況下,您並不特別關心壓縮什麼時候發生;您想設定任務並忘記它。
首先,您將定義Worker類,並重寫它的doWork()方法。您的worker類指定如何執行該操作,但沒有任何有關任務何時執行的資訊。
KOTLIN
class CompressWorker : Worker() {
override fun doWork(): Result {
// Do the work here--in this case, compress the stored images.
// In this example no parameters are passed; the task is
// assumed to be "compress the whole library."
myCompress()
// Indicate success or failure with your return value:
return Result.SUCCESS
// (Returning RETRY tells WorkManager to try this task again
// later; FAILURE says not to try again.)
}
}
JAVA
public class CompressWorker extends Worker {
@Override
public Worker.Result doWork() {
// Do the work here--in this case, compress the stored images.
// In this example no parameters are passed; the task is
// assumed to be "compress the whole library."
myCompress();
// Indicate success or failure with your return value:
return Result.SUCCESS;
// (Returning RETRY tells WorkManager to try this task again
// later; FAILURE says not to try again.)
}
}
接下來,基於Worker建立OneTimeWorkRequest物件,然後使用WorkManager將任務排到佇列中:
KOTLIN
val compressionWork = OneTimeWorkRequestBuilder<CompressWorker>().build()
WorkManager.getInstance().enqueue(compressionWork)
JAVA
OneTimeWorkRequest compressionWork =
new OneTimeWorkRequest.Builder(CompressWorker.class)
.build();
WorkManager.getInstance().enqueue(compressionWork);
WorkManager選擇適當的時間來執行任務,平衡諸如系統負載、裝置是否插入,等等。在大多數情況下,如果不指定任何約束,WorkManager立即執行您的任務。如果需要檢查任務狀態,可以通過獲取適當的LiveData控制代碼來獲得WorkStatus物件。例如,如果您想檢查任務是否已完成,您可以使用這樣的程式碼:
KOTLIN
WorkManager.getInstance().getStatusById(compressionWork.id)
.observe(lifecycleOwner, Observer { workStatus ->
// Do something with the status
if (workStatus != null && workStatus.state.isFinished) {
// ...
}
})
JAVA
WorkManager.getInstance().getStatusById(compressionWork.getId())
.observe(lifecycleOwner, workStatus -> {
// Do something with the status
if (workStatus != null && workStatus.getState().isFinished()) {
// ...
}
});
有關使用LiveData的更多資訊,請參見LiveData概述。
2.3 任務約束
如果希望,可以指定任務執行時的約束。例如,您可能希望指定該任務只在裝置空閒時執行,並連線到電源。在這種情況下,您需要建立一個OneTimeWorkRequest.Builder物件,並使用該生成器建立實際的OneTimeWorkRequest:
KOTLIN
// Create a Constraints object that defines when the task should run
val myConstraints = Constraints.Builder()
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
// Many other constraints are available, see the
// Constraints.Builder reference
.build()
// ...then create a OneTimeWorkRequest that uses those constraints
val compressionWork = OneTimeWorkRequestBuilder<CompressWorker>()
.setConstraints(myConstraints)
.build()
JAVA
// Create a Constraints object that defines when the task should run
Constraints myConstraints = new Constraints.Builder()
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
// Many other constraints are available, see the
// Constraints.Builder reference
.build();
// ...then create a OneTimeWorkRequest that uses those constraints
OneTimeWorkRequest compressionWork =
new OneTimeWorkRequest.Builder(CompressWorker.class)
.setConstraints(myConstraints)
.build();
然後像以前一樣將新的OneTimeWorkRequest請求物件傳遞給WorkManager.enqueue()。WorkManager在查詢執行任務的時間時會考慮約束。
2.4 取消任務
您可以在任務加入佇列後取消任務。要取消任務,您需要它的工作ID,您可以從WorkRequest物件中獲取它。例如,下面的程式碼取消了前一節的壓縮工作請求:
KOTLIN
val compressionWorkId:UUID = compressionWork.getId()
WorkManager.getInstance().cancelWorkById(compressionWorkId)
JAVA
UUID compressionWorkId = compressionWork.getId();
WorkManager.getInstance().cancelWorkById(compressionWorkId);
WorkManager盡最大努力取消任務,但這樣存在很大的不確定性——當您試圖取消任務時,任務可能已經執行或完成。WorkManager還提供方法,以取消唯一工作序列中的所有任務,或者取消具有指定標記的所有任務,這也是盡最大努力取消任務的基礎上。
2.5 Tagged work
您可以通過將標記字串分配給任何WorkRequest物件來對任務進行邏輯分組。若要設定標籤,請呼叫WorkRequest.Builder.addTag(),例如:
KOTLIN
val cacheCleanupTask =
OneTimeWorkRequestBuilder<MyCacheCleanupWorker>()
.setConstraints(myConstraints)
.addTag("cleanup")
.build()
JAVA
OneTimeWorkRequest cacheCleanupTask =
new OneTimeWorkRequest.Builder(MyCacheCleanupWorker.class)
.setConstraints(myConstraints)
.addTag("cleanup")
.build();
WorkManager類提供了幾種實用方法,允許您使用特定標記操作所有任務。例如,WorkManager.cancelAllWorkByTag()取消具有特定標記的所有任務,WorkManager.getStatusesByTag()返回具有該標記的所有任務的WorkStatus列表。
2.6 週期性任務
您可能有一個需要重複執行的任務。例如,照片管理器APP壓縮照片可能不止一次。更有可能的是,它會經常檢查其共享照片,並檢視是否有新的或更改的影象需要壓縮。這個重複的任務可以壓縮它找到的影象,或者,當它找到需要壓縮的影象時,它可以啟動新的“壓縮此影象”任務。
要建立迴圈任務,請使用PeriodicWorkRequest.Builder
類建立PeriodicWorkRequest
物件,然後以與OneTimeWorkRequest
物件相同的方式將PeriodicWorkRequest
加入佇列。例如,假設我們定義了一個PhotoCheckWorker
類來識別需要壓縮的影象。如果您想每12小時執行一次清單任務,您將建立一個像這樣的PeriodicWorkRequest
物件:
KOTLIN
val photoCheckBuilder =
PeriodicWorkRequestBuilder<PhotoCheckWorker>(12, TimeUnit.HOURS)
// ...if you want, you can apply constraints to the builder here...
// Create the actual work object:
val photoCheckWork = photoCheckBuilder.build()
// Then enqueue the recurring task:
WorkManager.getInstance().enqueue(photoCheckWork)
JAVA
new PeriodicWorkRequest.Builder photoCheckBuilder =
new PeriodicWorkRequest.Builder(PhotoCheckWorker.class, 12,
TimeUnit.HOURS);
// ...if you want, you can apply constraints to the builder here...
// Create the actual work object:
PeriodicWorkRequest photoCheckWork = photoCheckBuilder.build();
// Then enqueue the recurring task:
WorkManager.getInstance().enqueue(photoCheckWork);
WorkManager
試圖在您請求的時間間隔內執行您的任務,但要遵守您施加的約束以及其它要求。
3. WorkManager高階主題
WorkManager使建立和預約複雜的任務請求變得容易。您可以使用API來實現這樣的場景:
3.1 Chained tasks
應用程式可能需要按特定順序執行多個任務。WorkManager允許您建立多個任務並將其加入工作序列中,並指定它們應該執行的順序。
例如,假設應用程式有三個OneTimeWorkRequest物件:WorkA、WorkB和WorkC。任務必須按該順序執行。要使它們入隊,使用WorkManager.beginWith()方法建立一個序列,傳遞第一個OneTimeWorkRequest物件;該方法返回一個WorkContinuation物件,該物件定義一系列任務。然後按順序使用WorkContinuation.then()新增剩餘的OneTimeWorkRequest物件,最後使用WorkContinuation.enqueue()對整個序列進行入隊:
KOTLIN
WorkManager.getInstance()
.beginWith(workA)
// Note: WorkManager.beginWith() returns a
// WorkContinuation object; the following calls are
// to WorkContinuation methods
.then(workB) // FYI, then() returns a new WorkContinuation instance
.then(workC)
.enqueue()
JAVA
WorkManager.getInstance()
.beginWith(workA)
// Note: WorkManager.beginWith() returns a
// WorkContinuation object; the following calls are
// to WorkContinuation methods
.then(workB) // FYI, then() returns a new WorkContinuation instance
.then(workC)
.enqueue();
根據每個任務指定的約束,WorkManager按要求的順序執行任務。如果任何任務返回Worker.Result.FAILURE,則整個序列結束。
您還可以將多個OneTimeWorkRequest物件傳遞給任何beginWith()和.then()呼叫。如果將幾個OneTimeWorkRequest物件傳遞給一個方法呼叫,WorkManager在執行序列的其餘部分之前(並行地)執行所有這些任務。例如:
KOTLIN
WorkManager.getInstance()
// First, run all the A tasks (in parallel):
.beginWith(workA1, workA2, workA3)
// ...when all A tasks are finished, run the single B task:
.then(workB)
// ...then run the C tasks (in any order):
.then(workC1, workC2)
.enqueue()
JAVA
WorkManager.getInstance()
// First, run all the A tasks (in parallel):
.beginWith(workA1, workA2, workA3)
// ...when all A tasks are finished, run the single B task:
.then(workB)
// ...then run the C tasks (in any order):
.then(workC1, workC2)
.enqueue();
您可以通過使用WorkContinuation.combine()方法將多個鏈合併來建立更復雜的序列。例如,假設您想執行這樣的序列:
圖1 可以使用WorkContinuation設定複雜鏈式任務。
要設定這個序列,建立兩個獨立的鏈,然後將它們組合成第三個:
KOTLIN
val chain1 = WorkManager.getInstance()
.beginWith(workA)
.then(workB)
val chain2 = WorkManager.getInstance()
.beginWith(workC)
.then(workD)
val chain3 = WorkContinuation
.combine(chain1, chain2)
.then(workE)
chain3.enqueue()
JAVA
WorkContinuation chain1 = WorkManager.getInstance()
.beginWith(workA)
.then(workB);
WorkContinuation chain2 = WorkManager.getInstance()
.beginWith(workC)
.then(workD);
WorkContinuation chain3 = WorkContinuation
.combine(chain1, chain2)
.then(workE);
chain3.enqueue();
在這種情況下,WorkMeor在工作B之前執行WorkA。它還執行WorkC之前Word。工作和工作完成後,WorkMeor執行WORKE。
注意:雖然WorkManager按順序執行每個子鏈,但是不能保證鏈1中的任務如何與鏈2中的任務重疊。例如,WorkB可能在workC之前或之後執行,或者它們可能同時執行。唯一的保證是每個子鏈中的任務將按順序執行;也就是說,workB直到workA完成後才啟動。
存在一些特定情況,有一系列WorkContinuation方法提供短時處理。例如,有一個WorkContinuation.combine(OneTimeWorkRequest,WorkContinuation…)方法,它指示WorkManager完成所有指定的WorkContinuation鏈,然後以指定的OneTimeWorkRequest結束。有關詳細資訊,請參見WorkContinuation。
3.2 Unique工作序列
您可以建立一個Unique的工作序列,通過呼叫beginUniqueWork()而不是beginWith()啟動序列。每個唯一的工作序列都有一個名稱;WorkManager只允許一次使用一個名稱的工作序列。在建立新的唯一工作序列時,指定如果已經存在具有相同名稱的未完成序列,則WorkManager應該做什麼:
如果您有一個不應該多次排隊的任務,那麼Unique工作序列可能是有用的。例如,如果您的app需要將其資料同步到網路,則可以將名為“sync”的序列加入佇列,並指定如果已經存在具有該名稱的序列,則應忽略新任務。如果你需要逐步建立一個長的任務鏈,那麼Unique工作序列也是有用的。例如,一個圖片編輯應用程式可以讓使用者撤消一長串的動作。每一個撤消操作可能需要一段時間,但它們必須按正確的順序執行。在這種情況下,應用程式可以建立一個"undo"鏈,並根據需要將每個撤銷操作附加到鏈上。
3.3 輸入引數和返回值
為了獲得更大的靈活性,可以將引數傳遞給任務並使任務返回結果。傳遞和返回的值是鍵值對。若要將引數傳遞給任務,請在建立WorkRequest物件之前呼叫WorkRequest.Builder.setInputData()方法。該方法使用Data.Builder建立並持有資料物件。Work類可以通過呼叫Worker.getInputData()來訪問這些引數。為了輸出返回值,任務呼叫Worker.setOutputData(),它接受一個Data物件;您可以通過觀察任務的LiveData來獲得輸出。
例如,假設您有一個Worker類,執行耗時的計算。下面的程式碼顯示了Worker類的外觀:
KOTLIN
// Define the parameter keys:
const val KEY_X_ARG = "X"
const val KEY_Y_ARG = "Y"
const val KEY_Z_ARG = "Z"
// ...and the result key:
const val KEY_RESULT = "result"
// Define the Worker class:
class MathWorker : Worker() {
override fun doWork(): Result {
val x = inputData.getInt(KEY_X_ARG, 0)
val y = inputData.getInt(KEY_Y_ARG, 0)
val z = inputData.getInt(KEY_Z_ARG, 0)
// ...do the math...
val result = myCrazyMathFunction(x, y, z);
//...set the output, and we're done!
val output: Data = mapOf(KEY_RESULT to result).toWorkData()
setOutputData(output)
return Result.SUCCESS
}
}
JAVA
// Define the Worker class:
public class MathWorker extends Worker {
// Define the parameter keys:
public static final String KEY_X_ARG = "X";
public static final String KEY_Y_ARG = "Y";
public static final String KEY_Z_ARG = "Z";
// ...and the result key:
public static final String KEY_RESULT = "result";
@Override
public Worker.Result doWork() {
// Fetch the arguments (and specify default values):
int x = getInputData().getInt(KEY_X_ARG, 0);
int y = getInputData().getInt(KEY_Y_ARG, 0);
int z = getInputData().getInt(KEY_Z_ARG, 0);
// ...do the math...
int result = myCrazyMathFunction(x, y, z);
//...set the output, and we're done!
Data output = new Data.Builder()
.putInt(KEY_RESULT, result)
.build();
setOutputData(output);
return Result.SUCCESS;
}
}
要建立work並傳遞引數,您將使用這樣的程式碼:
KOTLIN
val myData: Data = mapOf("KEY_X_ARG" to 42,
"KEY_Y_ARG" to 421,
"KEY_Z_ARG" to 8675309)
.toWorkData()
// ...then create and enqueue a OneTimeWorkRequest that uses those arguments
val mathWork = OneTimeWorkRequestBuilder<MathWorker>()
.setInputData(myData)
.build()
WorkManager.getInstance().enqueue(mathWork)
JAVA
// Create the Data object:
Data myData = new Data.Builder()
// We need to pass three integers: X, Y, and Z
.putInt(KEY_X_ARG, 42)
.putInt(KEY_Y_ARG, 421)
.putInt(KEY_Z_ARG, 8675309)
// ... and build the actual Data object:
.build();
// ...then create and enqueue a OneTimeWorkRequest that uses those arguments
OneTimeWorkRequest mathWork = new OneTimeWorkRequest.Builder(MathWorker.class)
.setInputData(myData)
.build();
WorkManager.getInstance().enqueue(mathWork);
任務的WorkStatus中返回的值是可用:
KOTLIN
WorkManager.getInstance().getStatusById(mathWork.id)
.observe(this, Observer { status ->
if (status != null && status.state.isFinished) {
val m