WorkManager
1日に1回とか、何かを定期的に実行してほしい時があります。
バックグラウンドでユーザーの知らない間に、24時間に一回だけキャッシュの掃除をして欲しくて、手軽な形で使ってみました。
Lifecycle
と Livedata
、 Timber
(なくてもいい)はすでに入っている前提です。
// 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()
などとしてあげましょう。
あとは、基底クラスで呼び出してあげるだけでアプリが存在する限り、自動的に指定の処理を行ってくれます。
DailyWorkManager.startWork(lifecycleOwner = this , callback())
実行するだけでいいなら、もっとシンプルにできます。
ただ、端末のリソースや、設定した制約(Constraints
)により、実行タイミングがずれ込んだりもするようなので、確実に実行してほしい場合は、もう少し考え込む必要がありそうです。
手軽に使いたい場合、簡単で、すごく便利ですね。