WorkManager を使用すると、複雑なタスク リクエストを簡単にセットアップしてスケジュール設定できます。 次のようなシナリオでこの API を使用できます。
- タスクのシーケンス チェーンを、指定順序で実行する場合
- それぞれ一意の名前を持つシーケンスに対して、アプリが同じ名前のシーケンスを 2 つ開始したときの動作に関するルールを設定する場合
- タスクが値を渡したり戻したりする場合(たとえば、各タスクが引数をチェーン内の次のタスクに渡すタスクチェーンなど)
タスクチェーン
アプリによっては、複数のタスクを特定の順序で実行することが必要となる場合があります。
WorkManager
を使用すると、対象となる複数のタスクとその実行順序を指定する処理シーケンスを作成し、キューに登録できます。
たとえば、アプリに 3 つの OneTimeWorkRequest
オブジェクト(workA
、workB
、workC
)があり、各タスクをこの順序で実行する必要があるとします。各タスクをキューに登録するには、WorkManager.beginWith(OneTimeWorkRequest)
メソッドを使用してシーケンスを作成し、最初の OneTimeWorkRequest
オブジェクトを渡します。このメソッドが WorkContinuation
オブジェクトを返すことで、タスクのシーケンスが定義されます。次に、WorkContinuation.then(OneTimeWorkRequest)
を使用して残りの OneTimeWorkRequest
オブジェクトを順番に追加します。最後に、WorkContinuation.enqueue()
を使用してシーケンス全体をキューに登録します。
Kotlin
WorkManager.getInstance(myContext) .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(myContext) .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
は、各タスクの指定制約に従って、リクエストされた順序でタスクを実行します。いずれかのタスクが Result.failure()
を返すと、シーケンス全体が終了します。
また、複数の OneTimeWorkRequest
オブジェクトを beginWith(List<OneTimeWorkRequest>)
呼び出しや then(List<OneTimeWorkRequest>)
呼び出しに渡すこともできます。複数の OneTimeWorkRequest
オブジェクトを単一のメソッド呼び出しに渡すと、WorkManager
は、シーケンスの残りを実行する前に、渡されたタスクをすべて並列実行します。次に例を示します。
Kotlin
WorkManager.getInstance(myContext) // First, run all the A tasks (in parallel): .beginWith(Arrays.asList(workA1, workA2, workA3)) // ...when all A tasks are finished, run the single B task: .then(workB) // ...then run the C tasks (in parallel): .then(Arrays.asList(workC1, workC2)) .enqueue()
Java
WorkManager.getInstance(myContext) // First, run all the A tasks (in parallel): .beginWith(Arrays.asList(workA1, workA2, workA3)) // ...when all A tasks are finished, run the single B task: .then(workB) // ...then run the C tasks (in parallel): .then(Arrays.asList(workC1, workC2)) .enqueue();
WorkContinuation.combine(List<OneTimeWorkRequest>)
メソッドを使用すると、複数のチェーンを結合して、さらに複雑なシーケンスを作成できます。たとえば、次のようなシーケンスを実行するとします。
WorkContinuation
を使用して、複雑なタスクチェーンをセットアップできます。このシーケンスをセットアップするには、2 つの独立したチェーンを作成して、それを 3 つ目のチェーンに結合します。
Kotlin
val chain1 = WorkManager.getInstance(myContext) .beginWith(workA) .then(workB) val chain2 = WorkManager.getInstance(myContext) .beginWith(workC) .then(workD) val chain3 = WorkContinuation .combine(Arrays.asList(chain1, chain2)) .then(workE) chain3.enqueue()
Java
WorkContinuation chain1 = WorkManager.getInstance(myContext) .beginWith(workA) .then(workB); WorkContinuation chain2 = WorkManager.getInstance(myContext) .beginWith(workC) .then(workD); WorkContinuation chain3 = WorkContinuation .combine(Arrays.asList(chain1, chain2)) .then(workE); chain3.enqueue();
この場合、WorkManager
は workA
を実行してから workB
を実行します。また、workC
を実行してから workD
を実行します。workB
と workD
が両方とも終了した後、WorkManager
は workE
を実行します。
特定の状況に応じて簡略型を提供する WorkContinuation
メソッドには、さまざまなバリエーションがあります。詳しくは、WorkContinuation
のリファレンスをご覧ください。
一意処理シーケンス
beginWith(OneTimeWorkRequest)
呼び出しではなく beginUniqueWork(String, ExistingWorkPolicy, OneTimeWorkRequest)
呼び出しを使用してシーケンスを開始することにより、一意処理シーケンスを作成できます。一意処理シーケンスはそれぞれ固有の名前を持ちます。WorkManager
は、特定の名前の処理シーケンスを一度に 1 つだけ許可します。新しい一意処理シーケンスを作成する際は、すでに同じ名前の未完了シーケンスがある場合の WorkManager
の動作を指定します。
- REPLACE - 既存のシーケンスをキャンセルして、新しいシーケンスに置き換えます。
- KEEP - 既存のシーケンスを維持して、新しいリクエストを無視します。
- APPEND - 既存のシーケンスに新しいシーケンスを追加して、既存のシーケンスの最後のタスクが完了した後に新しいシーケンスの最初のタスクを実行します。
一意処理シーケンスは、複数回キューに登録すべきでないタスクがある場合に役立ちます。たとえば、データをネットワークに同期する必要があるアプリで「sync」という名前のシーケンスをキューに登録するとします。このとき、すでに同じ名前のシーケンスが存在する場合は、新しいタスクを無視するように指定できます。一意処理シーケンスは、長いタスクチェーンを段階的に構築する必要がある場合にも役立ちます。たとえば、写真編集アプリで、一連の編集を元に戻すことができる機能を提供しているとします。元に戻す操作はそれぞれ時間がかかる可能性がありますが、正しい順序で実行する必要があります。この場合、アプリは「元に戻す」用のチェーンを作成し、元に戻す各操作を必要に応じてチェーンに追加できます。
入力パラメータと戻り値
柔軟性を高めるため、引数を渡したタスクが結果を返すように指定できます。渡す値と戻り値はそれぞれ Key-Value ペアです。タスクに引数を渡すには、WorkRequest
オブジェクトを作成する前に WorkRequest.Builder.setInputData(Data)
メソッドを呼び出します。このメソッドは、Data
オブジェクト(Data.Builder
を使用して作成)を受け取ります。Worker
クラスは、Worker.getInputData()
を呼び出すことで、これらの引数にアクセスできます。戻り値を出力するには、タスクの Result
内に組み込む必要があります(たとえば、Result.success(Data)
)。タスクの WorkInfo
をモニタリングすることで、出力を取得できます。
たとえば、時間のかかる計算を行う 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(context : Context, params : WorkerParameters) : Worker(context, params) { 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 = myLongCalculation(x, y, z); //...set the output, and we're done! val output: Data = workDataOf(KEY_RESULT to result) return Result.success(output) } }
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"; public MathWorker( @NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @Override public 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 = myLongCalculation(x, y, z); //...set the output, and we're done! Data output = new Data.Builder() .putInt(KEY_RESULT, result) .build(); return Result.success(output); } }
処理を作成して引数を渡すには、次のようなコードを使用します。
Kotlin
val myData: Data = workDataOf("KEY_X_ARG" to 42, "KEY_Y_ARG" to 421, "KEY_Z_ARG" to 8675309) // ...then create and enqueue a OneTimeWorkRequest that uses those arguments val mathWork = OneTimeWorkRequestBuilder<MathWorker>() .setInputData(myData) .build() WorkManager.getInstance(myContext).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(myContext).enqueue(mathWork);
戻り値は、タスクの WorkInfo
から取得できます。
Kotlin
WorkManager.getInstance(myContext).getWorkInfoByIdLiveData(mathWork.id) .observe(this, Observer { info -> if (info != null && info.state.isFinished) { val myResult = info.outputData.getInt(KEY_RESULT, myDefaultValue) // ... do something with the result ... } })
Java
WorkManager.getInstance(myContext).getWorkInfoByIdLiveData(mathWork.getId()) .observe(lifecycleOwner, info -> { if (info != null && info.getState().isFinished()) { int myResult = info.getOutputData().getInt(KEY_RESULT, myDefaultValue)); // ... do something with the result ... } });
タスクをチェーン化した場合、1 つのタスクの出力を、チェーン内の次のタスクの入力として使用できます。単一の OneTimeWorkRequest
の後に別の単一の OneTimeWorkRequest
が続くシンプルなチェーンの場合、Result.success(Data)
を呼び出すことで、最初のタスクが結果を返し、getInputData()
を呼び出すことで、次のタスクが最初のタスクの結果を取得します。チェーンが複雑な場合(たとえば、複数のタスクが単一の後続タスクにすべての出力を送信する場合)、OneTimeWorkRequest.Builder
で InputMerger
を定義することで、複数のタスクが同一のキーを持つ出力を返した場合の動作を指定できます。
参考情報
WorkManager について詳しくは、以下の参考情報をご覧ください。
サンプル
- WorkManager サンプルアプリ
- Sunflower(Android Jetpack を使用した Android 開発のおすすめの方法を示すガーデニング アプリ)