Upgrade to Pro — share decks privately, control downloads, hide ads and more …

WorkManager のすすめ / Recommendation of WorkManager

WorkManager のすすめ / Recommendation of WorkManager

Naoya Shibahara

February 13, 2020
Tweet

More Decks by Naoya Shibahara

Other Decks in Programming

Transcript

  1. WorkManager のすすめ cookpad.apk #4

  2. 自己紹介 • 柴原 直也 • メディアプロダクト開発部 • Android アプリエンジニア •

    Kotlin
  3. WorkManager ? • Andoid Jetpack の1つ ◦ https://developer.android.com/topic/libraries/architecture/workmanager • 非同期の

    1 回限りのタスクや定期的なタスクのスケジュールを設定 • アプリやデバイスが再起動してもタスクを確実に実行 ◦ アップデートされても、予約したタスクは実行されます • ネットワークや充電の状態、Doze mode などの処理の制約を追加できる
  4. Worker • 実行する処理を書く場所 • Worker, CoroutineWorker, RxWorker, ListenableWorker がある ◦

    ListenableWorker は Worker, CoroutineWorker, RxWorker の親クラス • Result 型を返すことで処理が終了する ◦ Success, Failure, Retry が存在する
  5. class OneTimeWorker( appContext: Context, workerParameters: WorkerParameters ) : CoroutineWorker(appContext, workerParameters)

    { override suspend fun doWork(): Result { // do something return Result.success() or return retry() } private fun retry(): Result { return if (runAttemptCount < WORK_RETRY_COUNT) { Timber.i("Retry to Check Latest Version Work. Retrying count is ${runAttemptCount + 1} / $WORK_RETRY_COUNT.") Result.retry() } else { Result.failure() } } }
  6. WorkRequest • OneTimeWorkRequest ◦ 一度だけの処理を実行するときに使う • PeriodicWorkRequest ◦ 繰り返しの処理を実行するときに使う

  7. OneTimeWorkRequest • 一度だけの処理を実行するときに使う • 制約をつけられる ◦ ネットワーク接続時や充電中など

  8. class OneTimeWorker() : CoroutineWorker(appContext, workerParameters) { companion object { private

    const val WORK_ID = "MDM-WORKER-ONE-TIME" private const val WORK_RETRY_COUNT = 3 fun enqueue(context: Context) { val constraints = Constraints.Builder().apply { setRequiredNetworkType(NetworkType.CONNECTED) setRequiresCharging(true) }.build() val oneTimeWorkRequest = OneTimeWorkRequestBuilder<OneTimeWorker>().apply { addTag(WORK_TAG) setConstraints(constraints) }.build() val workManager = WorkManager.getInstance(context) workManager.enqueueUniqueWork( WORK_ID, ExistingWorkPolicy.REPLACE, oneTimeWorkRequest ) } } }
  9. PeriodicWorkRequest • 繰り返し実行 • 制約をつけられる ◦ ネットワーク接続時や充電中など • repeatInterval と

    flexInterval を指定することができる
  10. repeatInterval と flexInterval https://medium.com/androiddevelopers/workmanager-periodicity-ff35185ff006

  11. class PeriodicWorker() : CoroutineWorker(appContext, workerParameters) { companion object { private

    const val WORK_ID = "MDM-WORKER-SYSTEM-INFO" private const val REPEAT_INTERVAL_HOUR = 30L private const val FLEX_INTERVAL_MINUTES = 15L fun enqueue(context: Context) { val periodicWorkRequest = PeriodicWorkRequestBuilder<SystemInfoWorker>( REPEAT_INTERVAL_HOUR, TimeUnit.MINUTES, FLEX_INTERVAL_MINUTES, TimeUnit.MINUTES ).apply { addTag(WORK_ID) }.build() val workManager = WorkManager.getInstance(context) workManager.enqueueUniquePeriodicWork( WORK_ID, ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest ) } } }
  12. Worker は冪等にする • WorkManager は1度キューに登録されると、明示的にキャンセルしたりアプリを アンインストールするまで実行される • リトライすると同じパラメータで Worker の最初から再実行される

    • アプリアップデート時にもキャンセルはされずに残り続ける → 同じパラメータで何度実行されても、同じ状態になり、同じ結果が帰るようにする 必要がある
  13. リトライを制御する • 最大リトライ回数というのは、 WorkManager には定義されていない ◦ runAttemptCount という、現在の実行は何回目なのかという値が取れるの で、これを使って retry

    するか failure にするかを制御する • リトライするごとに、次のリトライ実行までの時間が増えていく ◦ BackoffPolicy があるので、これで次の実行までの時間を制御する
  14. BackoffPolicy • EXPONENTIAL ◦ 1回:30秒、2回:30*30秒、3回:30*30*30秒、... • LINEAR ◦ 1回:30秒、2回:30*2秒、3回:30*3秒、... •

    デフォルトは EXPONENTIAL で初回は 30 秒後 • 最大と最低実行時間も定義されており、これ以上これ以下は設定できない ◦ 最大:5時間、最小:10秒
  15. 毎日だいたい何時に実行したい • デイリージョブ ◦ 毎日朝8時に実行したい、という感じ • OneTimeRequest を使う ◦ WorkManager

    では時間の指定はできないので PeriodicRequest では実 現できない • setInitialDelay を使う ◦ 実行までの遅延時間を指定することができる
  16. class DailyWorker() : CoroutineWorker(appContext, workerParameters) { override suspend fun doWork():

    Result { val currentDate = ZonedDateTime.now() val dueDate = ZonedDateTime.now() .withHour(8) .withMinute(0) .withSecond(0) if (dueDate.isBefore(currentDate)) { dueDate.plusDays(1) } val timeDiff = ChronoUnit.MILLIS.between(currentDate, dueDate) val oneTimeWorkRequest = OneTimeWorkRequestBuilder<DailyWorker>().apply { addTag(WORK_TAG) setInitialDelay(timeDiff, TimeUnit.MILLISECONDS) }.build() val workManager = WorkManager.getInstance(context) workManager.enqueueWork(oneTimeWorkRequest) return Result.success() } } https://medium.com/androiddevelopers/workmanager-periodicity-ff35185ff006
  17. https://developer.android.com/guide/background

  18. まとめ • Worker は冪等に書くべき • リトライ便利 • CoroutineWorker, RxWorker があるので非同期処理が楽