WorkManager 程式庫是用於在 Android 中排程及執行可延後的背景作業。建議您針對 Firebase JobDispatcher 進行取代。下列指南將逐步說明將 Firebase JobDispatcher 執行遷移至 WorkManager 的程序。
Gradle 設定
如要將 WorkManager 程式庫匯入 Android 專案,請新增「開始使用 WorkManager」中列出的依附元件。
從 JobService 到工作站
FirebaseJobDispatcher
敬上
使用的子類別
JobService
。
做為定義需要完成的工作的進入點您可能直接使用 JobService
或使用 SimpleJobService
。
JobService
如下:
Kotlin
import com.firebase.jobdispatcher.JobParameters import com.firebase.jobdispatcher.JobService class MyJobService : JobService() { override fun onStartJob(job: JobParameters): Boolean { // Do some work here return false // Answers the question: "Is there still work going on?" } override fun onStopJob(job: JobParameters): Boolean { return false // Answers the question: "Should this job be retried?" } }
Java
import com.firebase.jobdispatcher.JobParameters; import com.firebase.jobdispatcher.JobService; public class MyJobService extends JobService { @Override public boolean onStartJob(JobParameters job) { // Do some work here return false; // Answers the question: "Is there still work going on?" } @Override public boolean onStopJob(JobParameters job) { return false; // Answers the question: "Should this job be retried?" } }
如果您使用 SimpleJobService
,就會覆寫 onRunJob()
,進而回傳 @JobResult int
類型。
主要差異在於直接使用 JobService
時,要在主執行緒上呼叫 onStartJob()
,而應用程式負責將工作卸載至背景執行緒。另一方面,如果您使用的是 SimpleJobService
,該服務就必須在背景執行緒上執行作業。
WorkManager 有類似的概念。WorkManager 的作業基本單位是 ListenableWorker
。另外還有其他實用的工作站子類型,例如 Worker
、RxWorker
和 CoroutineWorker
(使用 Kotlin 協同程式時)。
JobService 對應至 SenseableWorker
如果您直接使用 JobService
,則其對應的工作站為 ListenableWorker
。如果您使用的是 SimpleJobService
,請改用 Worker
。
讓我們使用上方的範例 (MyJobService
),並看看如何將其轉換為 ListenableWorker
。
Kotlin
import android.content.Context import androidx.work.ListenableWorker import androidx.work.ListenableWorker.Result import androidx.work.WorkerParameters import com.google.common.util.concurrent.ListenableFuture class MyWorker(appContext: Context, params: WorkerParameters) : ListenableWorker(appContext, params) { override fun startWork(): ListenableFuture<ListenableWorker.Result> { // Do your work here. TODO("Return a ListenableFuture<Result>") } override fun onStopped() { // Cleanup because you are being stopped. } }
Java
import android.content.Context; import androidx.work.ListenableWorker; import androidx.work.ListenableWorker.Result; import androidx.work.WorkerParameters; import com.google.common.util.concurrent.ListenableFuture; class MyWorker extends ListenableWorker { public MyWorker(@NonNull Context appContext, @NonNull WorkerParameters params) { super(appContext, params); } @Override public ListenableFuture<ListenableWorker.Result> startWork() { // Do your work here. Data input = getInputData(); // Return a ListenableFuture<> } @Override public void onStopped() { // Cleanup because you are being stopped. } }
WorkManager 的作業基本單位是 ListenableWorker
。跟 JobService.onStartJob()
一樣,其會在主執行緒上呼叫 startWork()
。此處的 MyWorker
導入 ListenableWorker
並回傳 ListenableFuture
的執行個體,可用來表示作業「非同步」完成。請在此選擇您專屬的執行緒策略。
這裡的 ListenableFuture
最終會回傳 ListenableWorker.Result
類型,可以是 Result.success()
、Result.success(Data outputData)
、Result.retry()
、Result.failure()
或 Result.failure(Data outputData)
其中之一。詳情請參閱 ListenableWorker.Result
的參考頁面。
因為無法符合限制條件 (例如網路已無法使用) 或是因為呼叫 WorkManager.cancel…()
方法,則呼叫 onStopped()
以表示 ListenableWorker
必須停止。如果作業系統因故決定關閉您的作業,則也可能呼叫 onStopped()
。
SimpleJobService 對應至工作站
使用 SimpleJobService
時,上述工作站如下所示:
Kotlin
import android.content.Context; import androidx.work.Data; import androidx.work.ListenableWorker.Result; import androidx.work.Worker; import androidx.work.WorkerParameters; class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) { override fun doWork(): Result { TODO("Return a Result") } override fun onStopped() { super.onStopped() TODO("Cleanup, because you are being stopped") } }
Java
import android.content.Context; import androidx.work.Data; import androidx.work.ListenableWorker.Result; import androidx.work.Worker; import androidx.work.WorkerParameters; class MyWorker extends Worker { public MyWorker(@NonNull Context appContext, @NonNull WorkerParameters params) { super(appContext, params); } @Override public Result doWork() { // Do your work here. Data input = getInputData(); // Return a ListenableWorker.Result Data outputData = new Data.Builder() .putString(“Key”, “value”) .build(); return Result.success(outputData); } @Override public void onStopped() { // Cleanup because you are being stopped. } }
這裡的 doWork()
會回傳 ListenableWorker.Result
的執行個體,表示作業同步完成。做法與 SimpleJobService
類似,其會在背景執行緒上安排工作。
JobBuilder 對應至 WorkRequest
FirebaseJobBuilder 使用 Job.Builder
來代表 Job
中繼資料。WorkManager 會使用 WorkRequest
來填入此角色。
WorkManager 提供兩種 WorkRequest
類型:OneTimeWorkRequest
和 PeriodicWorkRequest
。
如果您目前使用的是 Job.Builder.setRecurring(true)
,請建立新的 PeriodicWorkRequest
。否則,您應使用 OneTimeWorkRequest
。
現在就來看看使用 FirebaseJobDispatcher
排定複雜的 Job
究竟是什麼樣子:
Kotlin
val input: Bundle = Bundle().apply { putString("some_key", "some_value") } val job = dispatcher.newJobBuilder() // the JobService that will be called .setService(MyService::class.java) // uniquely identifies the job .setTag("my-unique-tag") // one-off job .setRecurring(false) // don't persist past a device reboot .setLifetime(Lifetime.UNTIL_NEXT_BOOT) // start between 0 and 60 seconds from now .setTrigger(Trigger.executionWindow(0, 60)) // don't overwrite an existing job with the same tag .setReplaceCurrent(false) // retry with exponential backoff .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) .setConstraints( // only run on an unmetered network Constraint.ON_UNMETERED_NETWORK, // // only run when the device is charging Constraint.DEVICE_CHARGING ) .setExtras(input) .build() dispatcher.mustSchedule(job)
Java
Bundle input = new Bundle(); input.putString("some_key", "some_value"); Job myJob = dispatcher.newJobBuilder() // the JobService that will be called .setService(MyJobService.class) // uniquely identifies the job .setTag("my-unique-tag") // one-off job .setRecurring(false) // don't persist past a device reboot .setLifetime(Lifetime.UNTIL_NEXT_BOOT) // start between 0 and 60 seconds from now .setTrigger(Trigger.executionWindow(0, 60)) // don't overwrite an existing job with the same tag .setReplaceCurrent(false) // retry with exponential backoff .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) // constraints that need to be satisfied for the job to run .setConstraints( // only run on an unmetered network Constraint.ON_UNMETERED_NETWORK, // only run when the device is charging Constraint.DEVICE_CHARGING ) .setExtras(input) .build(); dispatcher.mustSchedule(myJob);
如要透過 WorkManager 達成上述目標,您必須:
- 建立輸入資料,可用來做為
Worker
的輸入內容。 - 使用與上述
FirebaseJobDispatcher
定義的輸入資料和類似的限制條件,建立WorkRequest
。 - 將
WorkRequest
加入佇列。
設定工作站的輸入來源
FirebaseJobDispatcher
會使用 Bundle
將輸入資料傳送至 JobService
。WorkManager 改為使用 Data
。結果如下:
Kotlin
import androidx.work.workDataOf val data = workDataOf("some_key" to "some_val")
Java
import androidx.work.Data; Data input = new Data.Builder() .putString("some_key", "some_value") .build();
設定工作站的限制條件
FirebaseJobDispatcher
使用
Job.Builder.setConstaints(...)
設定工作限制條件。WorkManager 改為使用 Constraints
。
Kotlin
import androidx.work.* val constraints: Constraints = Constraints.Builder().apply { setRequiredNetworkType(NetworkType.CONNECTED) setRequiresCharging(true) }.build()
Java
import androidx.work.Constraints; import androidx.work.Constraints.Builder; import androidx.work.NetworkType; Constraints constraints = new Constraints.Builder() // The Worker needs Network connectivity .setRequiredNetworkType(NetworkType.CONNECTED) // Needs the device to be charging .setRequiresCharging(true) .build();
建立 WorkRequest (「一次性」或「定期」)
如要建立 OneTimeWorkRequest
和 PeriodicWorkRequest
,請使用 OneTimeWorkRequest.Builder
和 PeriodicWorkRequest.Builder
。
如要建立與上述 Job
類似的 OneTimeWorkRequest
,請按照下列指示操作:
Kotlin
import androidx.work.* import java.util.concurrent.TimeUnit val constraints: Constraints = TODO("Define constraints as above") val request: OneTimeWorkRequest = // Tell which work to execute OneTimeWorkRequestBuilder<MyWorker>() // Sets the input data for the ListenableWorker .setInputData(input) // If you want to delay the start of work by 60 seconds .setInitialDelay(60, TimeUnit.SECONDS) // Set a backoff criteria to be used when retry-ing .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30000, TimeUnit.MILLISECONDS) // Set additional constraints .setConstraints(constraints) .build()
Java
import androidx.work.BackoffCriteria; import androidx.work.Constraints; import androidx.work.Constraints.Builder; import androidx.work.NetworkType; import androidx.work.OneTimeWorkRequest; import androidx.work.OneTimeWorkRequest.Builder; import androidx.work.Data; // Define constraints (as above) Constraints constraints = ... OneTimeWorkRequest request = // Tell which work to execute new OneTimeWorkRequest.Builder(MyWorker.class) // Sets the input data for the ListenableWorker .setInputData(inputData) // If you want to delay the start of work by 60 seconds .setInitialDelay(60, TimeUnit.SECONDS) // Set a backoff criteria to be used when retry-ing .setBackoffCriteria(BackoffCriteria.EXPONENTIAL, 30000, TimeUnit.MILLISECONDS) // Set additional constraints .setConstraints(constraints) .build();
這裡的主要差異在於 WorkManager 的工作在所有裝置自動重新啟動時一律有效。
如要建立 PeriodicWorkRequest
,請執行下列動作:
Kotlin
val constraints: Constraints = TODO("Define constraints as above") val request: PeriodicWorkRequest = PeriodicWorkRequestBuilder<MyWorker>(15, TimeUnit.MINUTES) // Sets the input data for the ListenableWorker .setInputData(input) // Other setters .build()
Java
import androidx.work.BackoffCriteria; import androidx.work.Constraints; import androidx.work.Constraints.Builder; import androidx.work.NetworkType; import androidx.work.PeriodicWorkRequest; import androidx.work.PeriodicWorkRequest.Builder; import androidx.work.Data; // Define constraints (as above) Constraints constraints = ... PeriodicWorkRequest request = // Executes MyWorker every 15 minutes new PeriodicWorkRequest.Builder(MyWorker.class, 15, TimeUnit.MINUTES) // Sets the input data for the ListenableWorker .setInputData(input) . // other setters (as above) .build();
安排工作時間
定義 Worker
和 WorkRequest
後,您現在已可安排工作時間。
每個使用 FirebaseJobDispatcher
定義的 Job
都有一個 tag
,用於「識別」 Job
。此 API 還可讓應用程式告知排程器,如果 Job
的執行個體要取代 Job
的現有副本,請呼叫 setReplaceCurrent
。
Kotlin
val job = dispatcher.newJobBuilder() // the JobService that will be called .setService(MyService::class.java) // uniquely identifies the job .setTag("my-unique-tag") // don't overwrite an existing job with the same tag .setRecurring(false) // Other setters... .build()
Java
Job myJob = dispatcher.newJobBuilder() // the JobService that will be called .setService(MyJobService.class) // uniquely identifies the job .setTag("my-unique-tag") // don't overwrite an existing job with the same tag .setReplaceCurrent(false) // other setters // ... dispatcher.mustSchedule(myJob);
使用 WorkManager 時,您可以透過 enqueueUniqueWork()
和 enqueueUniquePeriodicWork()
API 達到相同的結果 (分別使用 OneTimeWorkRequest
和 PeriodicWorkRequest
時)。詳情請參閱 WorkManager.enqueueUniqueWork()
和 WorkManager.enqueueUniquePeriodicWork()
的參考頁面。
如下所示:
Kotlin
import androidx.work.* val request: OneTimeWorkRequest = TODO("A WorkRequest") WorkManager.getInstance(myContext) .enqueueUniqueWork("my-unique-name", ExistingWorkPolicy.KEEP, request)
Java
import androidx.work.ExistingWorkPolicy; import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; OneTimeWorkRequest workRequest = // a WorkRequest; WorkManager.getInstance(myContext) // Use ExistingWorkPolicy.REPLACE to cancel and delete any existing pending // (uncompleted) work with the same unique name. Then, insert the newly-specified // work. .enqueueUniqueWork("my-unique-name", ExistingWorkPolicy.KEEP, workRequest);
取消工作
有了 FirebaseJobDispatcher
,您就能使用下列方式取消作業:
Kotlin
dispatcher.cancel("my-unique-tag")
Java
dispatcher.cancel("my-unique-tag");
使用 WorkManager 時,您可以使用:
Kotlin
import androidx.work.WorkManager WorkManager.getInstance(myContext).cancelUniqueWork("my-unique-name")
Java
import androidx.work.WorkManager; WorkManager.getInstance(myContext).cancelUniqueWork("my-unique-name");
正在初始化 WorkManager
一般來說,WorkManager 通常會使用 ContentProvider
自行初始化。如要進一步控管 WorkManager 管理和排程作業的方式,您可以自訂 WorkManager 設定和初始化。