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

# [WorkManager](https://developer.android.com/reference/androidx/work/WorkManager)
1日に1回とか、何かを定期的に実行してほしい時があります。
バックグラウンドでユーザーの知らない間に、24時間に一回だけキャッシュの掃除をして欲しくて、手軽な形で使ってみました。

 `Lifecycle` と `Livedata` 、 `Timber`(なくてもいい)はすでに入っている前提です。

```gradle: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](https://developer.android.com/reference/androidx/work/WorkerParameters)** で、値を渡すことができます。

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

    override fun doWork(): Result {

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

**[enqueueUniquePeriodicWork](https://developer.android.com/reference/androidx/work/WorkManager.html#enqueueUniqueWork(java.lang.String,%2520androidx.work.ExistingWorkPolicy,%2520java.util.List%3Candroidx.work.OneTimeWorkRequest%3E))**は、一意の名前を付けられ、一つだけアクティブになり、`TimeUnit`で実行間隔を指定できます。 
（最短は、`PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS` -> 15分）

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

```kotlin:

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()を呼ぶ際に

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

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

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

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

***

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

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

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