ワークチェインにおけるワーカーの状態
ところで、第9回でも紹介したように、WorkManagerの実行状況は、Android Studioのバックグラウンドタスクインスペクタから確認できます。
そして、この記事にもあるように、ワークチェインの状況もこのバックグラウンドタスクインスペクタで確認できます。図2は、試しに、リスト1や2のコードでワークチェインを実行させた際のバックグラウンドタスクインスペクタでの表示です。左側のグラフ表示では、まさに図1と同じ図が表示されています。
図2は、CreateCountWorkerの実行が終了し、ReceiveCountWorkerがキュー状態の時点でのキャプチャです。それぞれのワーカー名の前の(Succeeded)や(Enqueued)記号がそのことを示しています。
FinishCountWorkerのは、Blockedという状態です。このFinishCountWorkerが選択された状態である図2の右側を見ると、State欄にBlockedという記述が見えます。
このBlockedは、ワーカーの状態を紹介した第9回の図1には登場しておらず、ワークチェイン独特の状態です。このBlockedは、ワーカーそのものの保留状態であり、より上流のワーカーの終了を待っている状態です。
そして、より上流のワーカーの処理が無事終了したSucceeded状態になって初めて、自身がEnqueued状態へと移行できるようになります(図3)。図2でReceiveCountWorkerがEnqueued状態なのは、上流のCreateCountWorkerがSucceededだからです。
では、あるワーカーの実行が失敗、すなわち、Failed状態になった場合はどうなるかというと、それより下流のワーカーは自動的にFailed状態となります。図4は、ReceiveCountWorkerで処理が失敗し、Failed状態になった場合のバックグラウンドタスクインスペクタでの表示です。FinishCountWorkerが自動的にFailed状態になっているのが読み取れます。
ワークチェインでデータを渡す方法
ワークチェインにおいて、上流のワーカーから下流のワーカーにデータを渡すこともできます。その際も、前回紹介したDataオブジェクトとそのビルダーを使います。
例えば、ReceiveCountWorker側でループ処理を行う際に表示させるメッセージ文字列と、そのループ処理回数の生成処理をCreateCountWorkerで行い、ReceiveCountWorkerに渡すとします。その場合、CreateCountWorkerのdoWork()メソッド内のコードは、Javaではリスト3、Kotlinではリスト4のようになります。
Data.Builder dataBuilder = new Data.Builder(); // (1) dataBuilder.putString("loopMsg", "こんにちは"); // (1) dataBuilder.putInt("loopCount", loopCount); // (1) Data outputData = dataBuilder.build(); // (1) return Result.success(outputData); // (2)
val dataBuilder = Data.Builder() // (1) dataBuilder.putString("loopMsg", "こんにちは") // (1) dataBuilder.putInt("loopCount", loopCount) // (1) val outputData = dataBuilder.build() // (1) return Result.success(outputData) // (2)
このコードの(1)の部分は、前回のリスト1やリスト2とほぼ同じコードです。違いは、最後の変数名がinputDataからoutputDataに変わっただけです。リスト3やリスト4のコードのポイントは(2)です。
これまで、ワーカー内の実行結果をリターンする値を生成するResult.success()などのメソッドには、引数は記述されていませんでした。これを、(2)のように、引数としてDateオブジェクトを渡すと、それがワークチェインの次のワーカーに渡されます。もちろん、次のワーカー(例えば今回でいえばReceiveCountWorker)内でのデータの受け取りコードは、前回のリスト3やリスト4と同じコードで取得できます。
並行処理ワークチェイン
ワークチェインは同時に複数のワーカーを実行することもできます。例えば、図5はCountWithIdWorkerを3個実行したのちにFinishCountWithIdWorkerを実行したワークチェインを、グラフ表示させたキャプチャです。
このような実行を行う場合のコードは、WorkManagerのbeginWith()メソッドにWorkRequestインスタンスのリストを渡します。上の例ですと、Javaではリスト5、Kotlinではリスト6のようになります。
List<OneTimeWorkRequest> workRequestList = Arrays.asList(countWithIdWork1Request, countWithIdWork2Request, countWithIdWork3Request); workManager.beginWith(workRequestList).then(finishCountWithIdWorkRequest).enqueue();
val workRequestList = listOf(countWithIdWork1Request, countWithIdWork2Request, countWithIdWork3Request) workManager.beginWith(workRequestList).then(finishCountWithIdWorkRequest).enqueue()
ポイントは、CountWithIdWorkerのWorkRequestを、countWithIdWork1Request、countWithIdWork2Request、countWithIdWork3Requestの3個生成しておき、そのリストオブジェクトをbeginWith()メソッドの引数として渡しているところです。もちろん、これらはそれぞれ別の種類のワーカーでもかまいません。いずれにせよ、リストを渡すことで、渡されたワーカーは並行処理を行います。
そして、すべてのワーカーの処理が成功した場合、下流のワーカー、例えば、リスト5やリスト6ならばthen()で渡されたFinishCountWithIdWorkerが実行されます。
並行処理における下流へのデータ渡し
ただし、このような並行処理では注意が必要です。並行処理それぞれから下流のワーカーにデータが渡され、Dataオブジェクトのキーが同じ場合、最後に実行されたワーカーのデータにその内容が上書きされてしまいます。例えば、CountWithIdWorker内で、次のコードのように自身のUUIDを下流のワーカーに渡すとします。
dataBuilder.putString("id", id.toString());
そうすると、Dataオブジェクトのキーidが重複します。この場合は、最後に実行したワーカーのUUIDしか下流は受け取ることができません。もちろんそれでかまわないケースもありますが、もし、それぞれのデータを受け取りたい場合は、それぞれのWorkRequestビルダーに対して、次のコードを実行します。
workRequestBuilder.setInputMerger(ArrayCreatingInputMerger.class);
こうすることでキーが重複した場合、自動的に配列として格納してくれます。下流のワーカーでデータを受け取る場合は、次のコードを記述することで、各CountWithIdWorkerのUUID値を取り出すことができます。
String[] idList = inputData.getStringArray("id");
なお、ここではJavaコードで記述していますが、Kotlinコードでも同じです。