Android Jetpack 初級 ( WorkManagerで、簡単定期実行 )


WorkManager

1日に1回とか、何かを定期的に実行してほしい時があります。
バックグラウンドでユーザーの知らない間に、24時間に一回だけキャッシュの掃除をして欲しくて、手軽な形で使ってみました。

LifecycleLivedataTimber(なくてもいい)はすでに入っている前提です。

build.gradle
    // AAC (Work Manager)
    implementation "android.arch.work:work-runtime:1.0.1"
    implementation "android.arch.work:work-runtime-ktx:1.0.1"

定期実行してもらうための内容を以下の形で実装します。
WorkerParameters で、値を渡すことができます。

class PreferenceWorker(val context: Context, workerParameters: WorkerParameters) :
        Worker(context, workerParameters) {

    override fun doWork(): Result {

        //TODO: 定期実行してほしい処理をここで行う
        return Result.success()
    }
}

enqueueUniquePeriodicWorkは、一意の名前を付けられ、一つだけアクティブになり、TimeUnitで実行間隔を指定できます。
(最短は、PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS -> 15分)

以下は、トップレベル関数にしてもいい気はします。


class DailyWorkerUtil {

    companion object {

        //WorkManagerが動く上での制約。デバイスがWiFiに接続されていて、ストレージが不足していない場合にのみ機能する。
        private fun createConstraints() =
                Constraints.Builder() // WIFIに接続している場合
                        .setRequiredNetworkType(NetworkType.UNMETERED) //その他の値(NOT_REQUIRED、CONNECTED、NOT_ROAMING、METERED)
                        .setRequiresBatteryNotLow(true) //電池残量が少なくない場合
                        .setRequiresStorageNotLow(true) //ストレージが不足していない場合
                        .build()

        //24時間に一回動くようにリクエストを設定する
        private fun createWorkRequest() =
                PeriodicWorkRequestBuilder<PreferenceWorker>(24, TimeUnit.HOURS)
                        .setConstraints(createConstraints())
                        //作業をやり直す必要がある場合に備えてバックオフを設定する
                        .setBackoffCriteria(BackoffPolicy.LINEAR, PeriodicWorkRequest.MIN_BACKOFF_MILLIS, TimeUnit.MILLISECONDS)

        //ジョブのスタート関数。LiveDataを扱わない場合、LifeCycleOwnerは不要。必要な場合、コールバックを高階関数で設定しておく。
        fun startWork(lifecycleOwner: LifecycleOwner, callback: () -> Unit) {

            //入力データを設定します。バンドルのようなもの。
            val work = createWorkRequest().build()

            val workManager = WorkManager.getInstance()

            //ExistingPeriodicWorkPolicy.KEEPは、このジョブがすでに存在する場合は保持される
            workManager
                    .enqueueUniquePeriodicWork(
                            "Batch Work", ExistingPeriodicWorkPolicy.KEEP, work)

            //WorkManager自体の実行結果をLiveDataで、Observeする。
            workManager
                    .getWorkInfoByIdLiveData(work.id)
                    .observe(lifecycleOwner, Observer { workInfo ->
                        if (workInfo != null && workInfo.state == WorkInfo.State.SUCCEEDED) {
                            //定期実行終了後に、行いたい処理をここで行う
                            callback()
                            Timber.d("WorkManagerInfo:Success${work.id}:${workInfo.outputData}")
                        } else {
                            Timber.d("WorkManagerInfo:Failed${work.id}:${workInfo.state}")
                        }
                    })
        }
    }
}

PeriodicWorkRequestBuilder.setInputData(任意のデータ : Data) で、先ほどのWorkerParametersに値を渡すことができるので、そうしたい場合はstartWork()を呼ぶ際に

// TODO: Dataに値を渡してあげる
val work = createWorkRequest(Data.EMPTY).build()

などとしてあげましょう。

あとは、基底クラスで呼び出してあげるだけでアプリが存在する限り、自動的に指定の処理を行ってくれます。

MainActivity.kt
DailyWorkManager.startWork(lifecycleOwner = this , callback())

実行するだけでいいなら、もっとシンプルにできます。

ただ、端末のリソースや、設定した制約(Constraints)により、実行タイミングがずれ込んだりもするようなので、確実に実行してほしい場合は、もう少し考え込む必要がありそうです。

手軽に使いたい場合、簡単で、すごく便利ですね。