Android JetpackのWorkManagerの使い方
JetpackはAndroidデベロッパーのために開発されたライブラリ、ツールなどのことを総称したものです。
今回はその中のWorkManagerという機能について説明します。
WorkManagerとは
APIの一つで、アプリが終了したり再起動しても延期できるタスクをスケジューリングする機能のようです。
定期的なサーバとの同期や、ログなどの送信に便利そうです。
下記が下位互換性
API 23 以上が搭載されたデバイスでは JobScheduler を使用
API 14~22 が搭載されたデバイスでは BroadcastReceiver と AlarmManager を組み合わせて使用
JobSchdulerなどは確かによく使われていましたが、これをさらにパッケージ化したものでしょうか。
ライブラリ追加
WorkManagerライブラリを使用するために、下記を追加してください。
def work_version = "2.3.4"
implementation "androidx.work:work-runtime:$work_version"
implementation "androidx.work:work-runtime-ktx:$work_version"
Worker
Workerでタスクを定義します。
doWorkメソッド内に実行したい処理を記述します。
そして、 Resultクラスでタスクの実行結果を受け取ることができます。
class MyWorkManager(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): Result {
// ここに実行したい処理を書く
return Result.success()
}
}
Resultクラスで返される状態は下記です
- Result.success(): タスクが正常に終了したかどうか
- Result.failure(): タスクが失敗したかどうか
- Result.retry(): 後でタスクを再試行する必要があるかどうか
周期的実行
タスクの実行には一回だけの実行と、周期的に実行できるオプションがあります。
WorkerRequestを使います
- OneTimeWorkRequest: 一回だけ
- PeriodicWorkRequest: 定期的
例えば10秒ごとの周期的なタスクにする場合は下記のように作ります。
val periodic: Duration = Duration.ofSeconds(10L)
val uploadWorkRequest = PeriodicWorkRequestBuilder<MyWorkManager>(periodic ).build()
もしこんなエラーが出たら、ここをみてください。
Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6. Please specify proper ‘-jvm-target’ option
その後、タスクをシステムにぶん投げます
WorkManager.getInstance(this).enqueue(uploadWorkRequest)
タスク実行タイミング
各タスクをデバイスの状態によって変えたい時に使えるのが、Constraintsクラスです。
- setRequiredNetworkType: ネットワーク状態
- setRequiresBatteryNotLow: 電池残量がたくさんある時
- setRequiresCharging: 電源に接続されてる時
- setRequiresDeviceIdle: デバイスがアイドル状態の時
- setRequiresStorageNotLow: ストレージ容量がたくさんある時
下記は一例です
var constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.setRequiresCharging(true)
.setRequiresDeviceIdle(true)
.setRequiresStorageNotLow(true)
.build();
val periodic: Duration = Duration.ofSeconds(10L)
val uploadWorkRequest = PeriodicWorkRequestBuilder<MyWorkManager>(periodic)
.setConstraints(constraints)
.build();
タスクを10分後に実行したい時は、下記を設定します。
val periodic: Duration = Duration.ofSeconds(10L)
val uploadWorkRequest = PeriodicWorkRequestBuilder<MyWorkManager>(periodic)
.setInitialDelay(10, TimeUnit.MINUTES)
.build()
タスク再試行時間
タスクを再試行したい場合には、WorkerクラスからResult.retry()を返すようです。
また、バックオフという遅延時間を設定することで、タスクの増加時間をずらすことができるようです。
val periodic: Duration = Duration.ofSeconds(10L)
val uploadWorkRequest = PeriodicWorkRequestBuilder<MyWorkManager>(periodic)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build()
Worker進捗・監視
Workerを進捗を設定するためにCoroutineWorkerクラスを使用します。
下記で0から100で進捗を設定することができます。
class ProgressWorker(context: Context, parameters: WorkerParameters) : CoroutineWorker(context, parameters) {
companion object {
const val Progress = "Progress"
private const val delayDuration = 1L
}
override suspend fun doWork(): Result {
val firstUpdate = workDataOf(Progress to 0)
val lastUpdate = workDataOf(Progress to 100)
setProgress(firstUpdate)
delay(delayDuration)
setProgress(lastUpdate)
return Result.success()
}
}
次にWorkerを監視するためには、observe機能を使用します。
observerにはLifecycleOwnerクラスを使用してタスクの状態を作成する必要があります。
getWorkInfoByIdLiveDataにはWorkerRequestのidを入れて下さい。
WorkManager.getInstance(applicationContext)
.getWorkInfoByIdLiveData(uploadWorkRequest.id)
.observe(observer, Observer { workInfo: WorkInfo? ->
if (workInfo != null) {
val progress = workInfo.progress
val value = progress.getInt(Progress, 0)
// 進捗状態を使った処理
}
})
上記でタスクを実行しながら状態をユーザーにお知らせすることが可能です。
まとめ
他にもいろいろ機能を追加することが可能ですが、基本的な使い方はここで終わりです。
これを使えば今まで非同期通信のスケジューリングなどが簡単に作成することが可能になりました。
以上です