優先実行ワーカー
ワークチェインの話はここまでとして、話題を優先実行ワーカーに変えます。
優先実行ワーカーとは
優先実行ワーカーは、公式サイトでExpedited Workと記載されているワーカーのことです。第9回で紹介したように、WorkRequestに各種設定を行うことで、ワーカーの実行タイミングをある程度制御できます。
しかし、ワーカー自体がそもそもバックグラウンドで動作することを前提としているため、当初は優先度の高いワーカーの実行向けの設定が存在していませんでした。その点を改善するために、WorkManagerのバージョン2.7より導入されたのが、このExpedited Work(優先実行ワーカー)です。
優先実行ワーカーの利用ケースとして、画像共有アプリなど、ユーザーが何かファイルをアップロードし、アップロードしたファイルの内容をすぐに画面に反映させる必要がある場合などを挙げることができます。他にも、公式ドキュメントには、決済アプリや登録アプリなどが例として挙げられています。これらのアプリでは、処理そのものは非同期、バックグラウンドで行うのが最適ですが、その処理結果の反映は、同期処理並の速さを求められます。そのような場合に、優先実行ワーカーを利用します。
優先実行ワーカーの設定
優先実行ワーカーを実行する場合、WorkRequestビルダーのsetExpedited()メソッドを使い、Java、Kotlinともにリスト7のコードとなります。
workRequestBuilder.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
このsetExpedited()メソッドは、引数としてOutOfQuotaPolicyの定数(より正確にはenum列挙子)を指定します。ここに登場したQuotaというのは、OSによって割り当てられる優先実行ワーカーのためのリソース(割り当て時間)のことを指します。ただし、これはワーカーを実行させるアプリがバックグラウンドに移行した場合の設定です。
例えば、画面にボタンを配置し、そのボタンをタップすると優先実行ワーカーが実行されるとします。そして、ボタンをタップした後も、アプリの画面を表示させた状態、すなわち、アプリがフォアグラウンドのままの場合は、優先実行ワーカーの処理時間が制限されることはありません。
一方アプリを終了したり、別のアプリを起動したりなど、アプリがバックグラウンドに回った場合は、Quotaという考えが有効になり、優先実行ワーカーは、このQuotaの範囲内で処理を終わらせる必要があります。そして、このQuotaの範囲内に処理が終了しなかった場合の挙動を指定するのが、setExpedited()メソッドの引数、すなわち、OutOfQuotaPolicyの列挙子です。
このOutOfQuotaPolicyの列挙子は以下の2個あり、そのどちらかを選択します。
-
RUN_AS_NON_EXPEDITED_WORK_REQUEST
優先実行ワーカーは、通常のワーカーとして扱われ、処理が継続されます。 -
DROP_WORK_REQUEST
優先実行ワーカーの処理はキャンセルされます。
繰り返し実行のWorkRequest
WorkManagerを紹介してきた本稿もこれで最後です。ここまでは、全てOneTimeWorkRequestに関するお話でした。最後にWorkManagerによるもうひとつのバックグラウンド処理であるPeriodicWorkRequestを紹介します。
PeriodicWorkRequestのWork States
PeriodicWorkRequestは、ワーカーを繰り返し実行するWorkRequestです。このWorkRequestを利用した場合、ワーカーの状態はOneTimeWorkRequestとは違い、非常にシンプルになります。
図6は、Androidの公式ドキュメントに掲載されている図です。
ENQUEUED、RUNNING、CANCELLEDの3種しかありません。PeriodicWorkRequestでは、ワーカーの処理が失敗しても成功しても、キューに戻され、設定された間隔で実行されます。もちろん、リトライでも同じです。そして、キャンセル処理が行われるまで、この繰り返しは続きます。
PeriodicWorkRequestの実行
このように繰り返しワーカーを実行する場合のコードは、Javaならばリスト8、Kotlinならばリスト9となります。
PeriodicWorkRequest.Builder workRequestBuilder = new PeriodicWorkRequest.Builder(CountWorker.class, 20, TimeUnit.MINUTES);
val workRequestBuilder = PeriodicWorkRequestBuilder<CountWorker>(20, TimeUnit.MINUTES)
WorkRequestのビルダーを生成する際、PeriodicWorkRequestのビルダーを生成するのがポイントです。Javaならば、PeriodicWorkRequest.Builderをnewします。一方、KotlinならばPeriodicWorkRequestBuilderインスタンスを生成します。
その際、Javaならば、第1引数に実行するワーカークラスを指定します(第2引数以降は後述します)。Kotlinならば、ジェネリクスとしてワーカークラスを型指定します。あとは、OneTimeWorkRequest同様に、PeriodicWorkRequestのビルダーからPeriodicWorkRequestを生成し、WorkManagerのキューに登録します。
繰り返し間隔を指定する引数
PeriodicWorkRequestは、ワーカーを繰り返し実行するためのWorkRequestです。この繰り返し実行する間隔を指定するのが、ビルダーを生成する際の引数です。
Javaの場合の第2引数とKotlinの場合の第1引数には数値を指定し、その数値が表す単位をJavaでは第3引数に、Kotlinでは第2引数に指定します。このパターンは、これまでも登場しているのでお分かりと思います。リスト8やリスト9では、数値は20、単位はTimeUnit.MINUTESとなっているので、20分間隔でワーカーが実行されることになります。
なお、繰り返しの間隔の最小値は、PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS定数で表されるミリセカンド値であり、現在900000ミリセカンド(=15分)となっています。
また、このPeriodicWorkRequestには、Flex Periodという考え方が導入されています。これは、実際にワーカーが実行されるタイミングに幅を持たせたものです。図7は、Androidの公式ドキュメントに掲載されている図です。
図7にある「interval」が繰り返し間隔であり、リスト8やリスト9では20分です。このうち、実際にワーカーが実行できるタイミングに幅を持たせたのが、図7中の「flex period can run work」の部分です。この範囲のどこかで、OSのタイミングがよいところで、ワーカーが実行されるようになり、この時間幅のことをフレックス間隔(Flex Interval)といいます。
このような実行間隔を指定する場合は、リスト8やリスト9のビルダーの生成コードにさらに引数を2個増やし、Javaならばリスト10、Kotlinならばリスト11のようなコードになります。この場合は、フレックス間隔として6分を指定しているため、20分中の14分を過ぎれば、それ以降どこかでワーカーが実行されることになります。
PeriodicWorkRequest.Builder workRequestBuilder = new PeriodicWorkRequest.Builder(CountWorker.class, 20, TimeUnit.MINUTES, 6, TimeUnit.MINUTES);
val workRequestBuilder = PeriodicWorkRequestBuilder<CountWorker>(20, TimeUnit.MINUTES, 6, TimeUnit.MINUTES)
なお、フレックス間隔を指定できる最小値は、PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS定数で表されるミリセカンド値であり、現在300000ミリセカンド(=5分)となっています。
まとめ
Android Jetpackについて紹介していく本連載の第11回は、いかがでしたでしょうか。今回は3回にわたって紹介したWorkManagerの最後として、ワークチェイン、優先実行ワーカー、ワーカーの繰り返し実行を紹介しました。次回はPagingを紹介します。