WorkManager は、Android で遅延可能なバックグラウンド処理をスケジューリングし実行するためのライブラリです。Firebase JobDispatcher の代わりに使用することが推奨されています。以下のガイドでは、Firebase JobDispatcher による実装を WorkManager に移行する手順を説明します。
Gradle のセットアップ
WorkManager ライブラリを Android プロジェクトにインポートするには、WorkManager のスタートガイドに記載されている依存関係を追加します。
JobService から Worker へ
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
を使用している場合、@JobResult int
型を返す onRunJob()
をオーバーライドします。
主な違いは、JobService
を直接使用している場合、onStartJob()
はメインスレッドから呼び出され、処理をバックグラウンド スレッドにオフロードするのはアプリの責任になるという点です。これに対して、SimpleJobService
を使用している場合、バックグラウンド スレッドで処理を実行するのはこのサービスの責任になります。
WorkManager にも同様のコンセプトがあります。WorkManager の基本の処理単位は ListenableWorker
です。ワーカーには、ほかにも Worker
、RxWorker
、CoroutineWorker
(Kotlin のコルーチンを使用する場合)のような便利なサブタイプもあります。
JobService を ListenableWorker に変換する
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
のリファレンス ページをご覧ください。
onStopped()
は、条件が満たされなくなったか(ネットワークが利用できなくなったなど)、あるいは WorkManager.cancel…()
メソッドが呼び出されたかのいずれかの理由で ListenableWorker
が停止する必要があることを通知するために呼び出されます。また、onStopped()
は、OS がなんらかの理由で処理の終了を決定した場合にも呼び出されます。
SimpleJobService を Worker に変換する
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 には、OneTimeWorkRequest
と PeriodicWorkRequest
の 2 種類の WorkRequest
があります。
現在 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();
ワーカーに Constraints をセットアップする
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(OneTime または Periodic)の作成
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
には、Job
の一意な識別に使用される tag
があります。また、アプリが setReplaceCurrent
を呼び出して、Job
のこのインスタンスが Job
の既存のコピーを置き換えるかどうかをスケジューラに通知する方法を提供しています。
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 の構成と初期化をカスタマイズできます。