Android Kotlin の基礎 09.2: WorkManager

この Codelab は、Android Kotlin の基礎コースの一部です。このコースを最大限に活用するには、Codelab を順番に進めることをおすすめします。コースのすべての Codelab は、Android Kotlin の基礎の Codelab のランディング ページに一覧表示されています。

はじめに

現実世界のほとんどのアプリでは、長時間実行されるバックグラウンド タスクを実行する必要があります。たとえば、アプリがファイルをサーバーにアップロードしたり、サーバーからデータを同期して Room データベースに保存したり、ログをサーバーに送信したり、データに対してコストのかかるオペレーションを実行したりする場合があります。このようなオペレーションは、UI スレッド(メインスレッド)から離れたバックグラウンドで実行する必要があります。バックグラウンド タスクは、RAM や電池などデバイスの限られたリソースを消費します。正しく処理しないと、ユーザーの利便性が低下する可能性があります。

この Codelab では、WorkManager を使用して、バックグラウンド タスクを最適化された効率的な方法でスケジュール設定する方法を学びます。Android でのバックグラウンド処理に利用できる他のソリューションについて詳しくは、バックグラウンド処理ガイドをご覧ください。

前提となる知識

  • ViewModelLiveDataRoom の Android アーキテクチャ コンポーネントの使用方法。
  • LiveData クラスで変換を行う方法。
  • コルーチンを作成して起動する方法。
  • データ バインディングでバインディング アダプターを使用する方法。
  • リポジトリ パターンを使用してキャッシュに保存されたデータを読み込む方法。

学習内容

  • 作業単位を表す Worker を作成する方法。
  • 実行する作業をリクエストする WorkRequest を作成する方法。
  • WorkRequest制約を追加して、ワーカーの実行方法とタイミングを定義する方法。
  • WorkManager を使用してバックグラウンド タスクをスケジュール設定する方法。

演習内容

  • バックグラウンド タスクを実行して、ネットワークから DevBytes 動画の再生リストをプリフェッチするワーカーを作成します。
  • ワーカーを定期的に実行するようにスケジュールします。
  • WorkRequest に制約を追加します。
  • 1 日 1 回実行される定期的な WorkRequest をスケジュールします。

この Codelab では、前の Codelab で作成した DevBytes アプリを使用します。(このアプリがない場合は、このレッスンのスターター コードをダウンロードできます)。

DevBytes アプリは、Google Android デベロッパー リレーションズ チームが作成した短いチュートリアルである DevByte 動画のリストを表示します。この動画では、Android 開発のデベロッパー向け機能とベスト プラクティスを紹介します。

動画を 1 日 1 回プリフェッチすることで、アプリのユーザー エクスペリエンスを向上させます。これにより、ユーザーがアプリを開くとすぐに新しいコンテンツが表示されます。

このタスクでは、スターター コードをダウンロードして確認します。

ステップ 1: スターター アプリをダウンロードして実行する

前の Codelab で作成した DevBytes アプリ(ある場合)を引き続き使用できます。または、スターター アプリをダウンロードすることもできます。

このタスクでは、スターター アプリをダウンロードして実行し、スターター コードを確認します。

  1. DevBytes アプリをまだお持ちでない場合は、GitHub の DevBytesRepository プロジェクトから、この Codelab の DevBytes スターター コードをダウンロードしてください。
  2. コードの ZIP ファイルを展開し、Android Studio でプロジェクトを開きます。
  3. テストデバイスまたはエミュレータがインターネットに接続されていない場合は、接続します。アプリをビルドして実行します。アプリはネットワークから DevByte 動画のリストを取得して表示します。
  4. アプリで動画をタップすると、YouTube アプリで開きます。

ステップ 2: コードを調べる

スターター アプリには、前の Codelab で紹介したコードが多数含まれています。この Codelab のスターター コードには、ネットワーキング、ユーザー インターフェース、オフライン キャッシュ、リポジトリの各モジュールがあります。WorkManager を使用してバックグラウンド タスクのスケジュール設定に集中できます。

  1. Android Studio で、すべてのパッケージを展開します。
  2. database パッケージを確認します。このパッケージには、データベース エンティティとローカル データベースが含まれています。ローカル データベースは Room を使用して実装されています。
  3. repository パッケージを確認します。このパッケージには、アプリの他の部分からデータレイヤーを抽象化する VideosRepository クラスが含まれています。
  4. 残りのスターター コードは、前の Codelab を参考にしながら、ご自身で確認してください。

WorkManagerAndroid アーキテクチャ コンポーネントの 1 つであり、Android Jetpack の一部です。WorkManager は、遅延可能で実行保証が必要なバックグラウンド処理に使用します。

  • Deferrable は、作業をすぐに実行する必要がないことを意味します。たとえば、分析データをサーバーに送信したり、バックグラウンドでデータベースを同期したりする作業は、延期できます。
  • 確実な実行とは、アプリが終了したりデバイスが再起動したりしてもタスクが実行されることを意味します。

WorkManager は、バックグラウンド処理を実行する際に、互換性の問題やバッテリーとシステムの健全性に関するベスト プラクティスを処理します。WorkManager は API レベル 14 までの下位互換性を提供します。WorkManager は、デバイスの API レベルに応じて、バックグラウンド タスクをスケジュールする適切な方法を選択します。JobScheduler(API 23 以上)または AlarmManagerBroadcastReceiver の組み合わせを使用する場合があります。

WorkManager では、バックグラウンド タスクを実行するタイミングの条件を設定することもできます。たとえば、バッテリー ステータス、ネットワーク ステータス、充電状態が特定の条件を満たしている場合にのみタスクを実行できます。制約の設定方法については、この Codelab の後半で説明します。

この Codelab では、DevBytes 動画プレイリストをネットワークから 1 日に 1 回プリフェッチするタスクをスケジュールします。このタスクをスケジュールするには、WorkManager ライブラリを使用します。

  1. build.gradle (Module:app) ファイルを開き、WorkManager 依存関係をプロジェクトに追加します。

    ライブラリの最新バージョンを使用すると、ソリューション アプリは想定どおりにコンパイルされます。そうでない場合は、問題を解決するか、以下のライブラリ バージョンに戻してみてください。
// WorkManager dependency
def work_version = "1.0.1"
implementation "android.arch.work:work-runtime-ktx:$work_version"
  1. プロジェクトを同期して、コンパイル エラーがないことを確認します。

プロジェクトにコードを追加する前に、WorkManager ライブラリの次のクラスについて理解しておいてください。

  • Worker
    このクラスで、バックグラウンドで実行する実際の処理(タスク)を定義します。このクラスを拡張して doWork() メソッドをオーバーライドします。doWork() メソッドには、サーバーとのデータの同期や画像の処理など、バックグラウンドで実行するコードを記述します。このタスクでは、Worker を実装します。
  • WorkRequest
    このクラスは、バックグラウンドでワーカーを実行するリクエストを表します。WorkRequest を使用して、デバイスが接続されているか、Wi-Fi が接続されているかなどの Constraints を利用して、ワーカー タスクを実行する方法とタイミングを設定します。WorkRequest は後のタスクで実装します。
  • WorkManager
    このクラスは WorkRequest をスケジュールして実行します。WorkManager は、指定された制約を尊重しながら、負荷がシステム リソースに分散されるよう作業リクエストをスケジュールします。WorkManager は後のタスクで実装します。

ステップ 1: ワーカーを作成する

このタスクでは、Worker を追加して、DevBytes 動画プレイリストをバックグラウンドでプリフェッチします。

  1. devbyteviewer パッケージ内に、work という新しいパッケージを作成します。
  2. work パッケージ内に、RefreshDataWorker という名前の新しい Kotlin クラスを作成します。
  3. RefreshDataWorker クラスを CoroutineWorker クラスから拡張します。contextWorkerParameters をコンストラクタ パラメータとして渡します。
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {
}
  1. 抽象クラスのエラーを解決するには、RefreshDataWorker クラス内で doWork() メソッドをオーバーライドします。
override suspend fun doWork(): Result {
  return Result.success()
}

suspend 関数は、一時停止して後で再開できる関数です。中断関数は、メインスレッドをブロックせずに長時間実行オペレーションを実行し、その完了を待つことができます。

ステップ 2: doWork() を実装する

Worker クラス内の doWork() メソッドはバックグラウンド スレッドで呼び出されます。このメソッドは同期的に処理を行い、ListenableWorker.Result オブジェクトを返す必要があります。Android システムは、Worker に最大 10 分間の実行時間を与え、ListenableWorker.Result オブジェクトを返します。この時間が経過すると、システムは Worker を強制的に停止します。

ListenableWorker.Result オブジェクトを作成するには、次のいずれかの静的メソッドを呼び出して、バックグラウンド作業の完了ステータスを示します。

  • Result.success() - 作業が正常に完了しました。
  • Result.failure() - 永続的なエラーで作業が完了しました。
  • Result.retry() - 一時的なエラーが発生したため、再試行する必要があります。

このタスクでは、ネットワークから DevBytes 動画プレイリストを取得する doWork() メソッドを実装します。VideosRepository クラスの既存のメソッドを再利用して、ネットワークからデータを取得できます。

  1. RefreshDataWorker クラスの doWork() 内で、VideosDatabase オブジェクトと VideosRepository オブジェクトを作成してインスタンス化します。
override suspend fun doWork(): Result {
   val database = getDatabase(applicationContext)
   val repository = VideosRepository(database)

   return Result.success()
}
  1. RefreshDataWorker クラスの doWork() 内で、return ステートメントの上に、try ブロック内で refreshVideos() メソッドを呼び出します。ワーカーが実行されたタイミングを追跡するログを追加します。
try {
   repository.refreshVideos( )
   Timber.d("Work request for sync is run")
   } catch (e: HttpException) {
   return Result.retry()
}

「未解決の参照」エラーを解決するには、retrofit2.HttpException をインポートします。

  1. 参考のため、RefreshDataWorker クラス全体を次に示します。
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {

   override suspend fun doWork(): Result {
       val database = getDatabase(applicationContext)
       val repository = VideosRepository(database)
       try {
           repository.refreshVideos()
       } catch (e: HttpException) {
           return Result.retry()
       }
       return Result.success()
   }
}

Worker は作業単位を定義し、WorkRequest は作業を実行する方法とタイミングを定義します。WorkRequest クラスには次の 2 つの具象実装があります。

  • OneTimeWorkRequest クラスは、1 回限りのタスク用です。(単発タスクは 1 回だけ実行されます)。
  • PeriodicWorkRequest クラスは、一定の間隔で繰り返される定期的な作業用です。

タスクは 1 回だけ実行されるものと、定期的に実行されるものがあります。クラスを適切に選択してください。繰り返し作業のスケジュール設定について詳しくは、繰り返し作業に関するドキュメントをご覧ください。

このタスクでは、前のタスクで作成したワーカーを実行する WorkRequest を定義してスケジュールします。

ステップ 1: 定期的な作業を設定する

Android アプリ内では、Application クラスは、アクティビティやサービスなどの他のすべてのコンポーネントを含む基本クラスです。アプリケーションまたはパッケージのプロセスが作成されると、Application クラス(または Application のサブクラス)が他のクラスよりも先にインスタンス化されます。

このサンプルアプリでは、DevByteApplication クラスは Application クラスのサブクラスです。DevByteApplication クラスは WorkManager をスケジュールするのに適しています。

  1. DevByteApplication クラスで、定期的なバックグラウンド処理を設定する setupRecurringWork() というメソッドを作成します。
/**
* Setup WorkManager background job to 'fetch' new network data daily.
*/
private fun setupRecurringWork() {
}
  1. setupRecurringWork() メソッド内で、PeriodicWorkRequestBuilder() メソッドを使用して、1 日に 1 回実行する定期的な作業リクエストを作成して初期化します。前のタスクで作成した RefreshDataWorker クラスを渡します。TimeUnit.DAYS の時間単位で 1 の繰り返し間隔を渡します。
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .build()

エラーを解決するには、java.util.concurrent.TimeUnit をインポートします。

ステップ 2: WorkManager で WorkRequest をスケジュール設定する

WorkRequest を定義したら、enqueueUniquePeriodicWork() メソッドを使用して、WorkManager でスケジュールを設定できます。このメソッドを使用すると、一意の名前を持つ PeriodicWorkRequest をキューに追加できます。特定の名前の PeriodicWorkRequest は一度に 1 つだけアクティブにできます。

たとえば、アクティブな同期オペレーションを 1 つだけにしたい場合があります。同期オペレーションが 1 つ保留中の場合は、ExistingPeriodicWorkPolicy を使用して、そのオペレーションを実行するか、新しい処理に置き換えるかを選択できます。

WorkRequest のスケジュール設定方法の詳細については、WorkManager のドキュメントをご覧ください。

  1. RefreshDataWorker クラスの先頭に、コンパニオン オブジェクトを追加します。このワーカーを一意に識別するワーク名を定義します。
companion object {
   const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}
  1. DevByteApplication クラスの setupRecurringWork() メソッドの最後に、enqueueUniquePeriodicWork() メソッドを使用して作業をスケジュールします。ExistingPeriodicWorkPolicy に KEEP 列挙型を渡します。PeriodicWorkRequest パラメータとして repeatingRequest を渡します。
WorkManager.getInstance().enqueueUniquePeriodicWork(
       RefreshDataWorker.WORK_NAME,
       ExistingPeriodicWorkPolicy.KEEP,
       repeatingRequest)

同じ名前の保留中(未完了)の作業が存在する場合、ExistingPeriodicWorkPolicy.KEEP パラメータにより、WorkManager は以前の定期的な作業を保持し、新しい作業リクエストを破棄します。

  1. DevByteApplication クラスの先頭で、CoroutineScope オブジェクトを作成します。コンストラクタ パラメータとして Dispatchers.Default を渡します。
private val applicationScope = CoroutineScope(Dispatchers.Default)
  1. DevByteApplication クラスに、コルーチンを開始するための delayedInit() という新しいメソッドを追加します。
private fun delayedInit() {
   applicationScope.launch {
   }
}
  1. delayedInit() メソッド内で、setupRecurringWork() を呼び出します。
  2. Timber の初期化を onCreate() メソッドから delayedInit() メソッドに移動します。
private fun delayedInit() {
   applicationScope.launch {
       Timber.plant(Timber.DebugTree())
       setupRecurringWork()
   }
}
  1. DevByteApplication クラスの onCreate() メソッドの最後に、delayedInit() メソッドの呼び出しを追加します。
override fun onCreate() {
   super.onCreate()
   delayedInit()
}
  1. Android Studio ウィンドウの下部にある [Logcat] ペインを開きます。RefreshDataWorker でフィルタします。
  2. アプリを実行します。WorkManager は繰り返し処理をすぐにスケジュールします。

    [Logcat] ペインで、WorkRequest がスケジュールされ、正常に実行されたことを示すログステートメントを確認します。
D/RefreshDataWorker: Work request for sync is run
I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

WM-WorkerWrapper ログは WorkManager ライブラリから表示されるため、このログメッセージを変更することはできません。

ステップ 3:(省略可)最小間隔で WorkRequest をスケジュールする

このステップでは、時間間隔を 1 日から 15 分に短縮します。これは、定期的な作業リクエストのログを実際に確認できるようにするためです。

  1. DevByteApplication クラスの setupRecurringWork() メソッド内で、現在の repeatingRequest の定義をコメントアウトします。定期的な繰り返し間隔が 15 分の新しい作業リクエストを追加します。
// val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
//        .build()
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
       .build()
  1. Android Studio で [Logcat] ペインを開き、RefreshDataWorker でフィルタします。以前のログを消去するには、[Clear logcat] アイコン をクリックします。
  2. アプリを実行すると、WorkManager が繰り返し処理をすぐにスケジュールします。[Logcat] ペインで、ログを確認します。Work Request が 15 分ごとに 1 回実行されていることがわかります。15 分待ってから、別のワーク リクエスト ログのセットを確認します。アプリを実行したままにしても、閉じても、WorkManager は実行されます。

    なお、間隔は 15 分未満になることもあれば、15 分を超えることもあります。(正確なタイミングは OS のバッテリー最適化によって異なります)。
12:44:40 D/RefreshDataWorker: Work request for sync is run
12:44:40 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
12:59:24 D/RefreshDataWorker: Work request for sync is run
12:59:24 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:15:03 D/RefreshDataWorker: Work request for sync is run
13:15:03 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:29:22 D/RefreshDataWorker: Work request for sync is run
13:29:22 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:44:26 D/RefreshDataWorker: Work request for sync is run
13:44:26 I/WM-WorkerWrapper: Worker result SUCCESS for Work
 

これで、ワーカーを作成し、WorkManager で処理リクエストのスケジュールを設定しました。ただし、制約が指定されていません。WorkManager は、デバイスのバッテリー残量が少ない場合や、デバイスがスリープ状態の場合、ネットワークに接続されていない場合でも、1 日 1 回作業をスケジュールします。この場合、デバイスのバッテリーとパフォーマンスに影響し、ユーザー エクスペリエンスが低下する可能性があります。

次のタスクでは、制約を追加してこの問題に対処します。

前のタスクでは、WorkManager を使用して作業リクエストをスケジュールしました。このタスクでは、処理を実行するタイミングの条件を追加します。

WorkRequest を定義するときに、Worker を実行するタイミングの制約を指定できます。たとえば、デバイスがアイドル状態のときのみ、またはデバイスが電源に接続されて Wi-Fi に接続されているときのみ処理を実行するように指定できます。作業の再試行のバックオフ ポリシーを指定することもできます。サポートされている制約は、Constraints.Builder の set メソッドです。詳細については、WorkRequest の定義をご覧ください。

ステップ 1: Constraints オブジェクトを追加して制約を 1 つ設定する

このステップでは、Constraints オブジェクトを作成し、オブジェクトに 1 つの制約(ネットワーク タイプの制約)を設定します。(制約が 1 つだけのログのほうが気づきやすいです。他の制約は後のステップで追加します)。

  1. DevByteApplication クラスの setupRecurringWork() の先頭で、Constraints 型の val を定義します。Constraints.Builder() メソッドを使用します。
val constraints = Constraints.Builder()

エラーを解決するには、androidx.work.Constraints をインポートします。

  1. setRequiredNetworkType() メソッドを使用して、constraints オブジェクトにネットワーク タイプの制約を追加します。UNMETERED 列挙型を使用すると、デバイスが定額制のネットワークに接続されている場合にのみ処理リクエストが実行されます。
.setRequiredNetworkType(NetworkType.UNMETERED)
  1. build() メソッドを使用して、ビルダーから制約を生成します。
val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .build()

次に、新しく作成した Constraints オブジェクトを作業リクエストに設定する必要があります。

  1. DevByteApplication クラスの setupRecurringWork() メソッド内で、Constraints オブジェクトを定期的な処理リクエスト repeatingRequest に設定します。制約を設定するには、build() メソッド呼び出しの上に setConstraints() メソッドを追加します。
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
               .setConstraints(constraints)
               .build()

ステップ 2: アプリを実行してログを確認する

このステップでは、アプリを実行し、制約付きの作業リクエストがバックグラウンドで一定の間隔で実行されていることを確認します。

  1. デバイスまたはエミュレータからアプリをアンインストールして、以前にスケジュール設定されたタスクをキャンセルします。
  2. Android Studio で [Logcat] ペインを開きます。[Logcat] ペインで、左側の [Logcat をクリア] アイコン をクリックして、以前のログをクリアします。work でフィルタします。
  3. 制約の仕組みを確認できるように、デバイスまたはエミュレータの Wi-Fi をオフにします。現在のコードでは、リクエストが従量制課金ネットワークでのみ実行されるように、制約が 1 つだけ設定されています。Wi-Fi がオフになっているため、デバイスは従量制または定額制のネットワークに接続されていません。そのため、この制約は満たされません。
  4. アプリを実行し、[Logcat] ペインを確認します。WorkManager はバックグラウンド タスクをすぐにスケジュールします。ネットワーク制約が満たされていないため、タスクは実行されません。
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
  1. デバイスまたはエミュレータで Wi-Fi をオンにして、[Logcat] ペインを確認します。これで、ネットワーク制約が満たされている限り、スケジュール設定されたバックグラウンド タスクが約 15 分ごとに実行されるようになります。
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
11:31:47 D/RefreshDataWorker: Work request for sync is run
11:31:47 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]
11:46:45 D/RefreshDataWorker: Work request for sync is run
11:46:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:03:05 D/RefreshDataWorker: Work request for sync is run
12:03:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:16:45 D/RefreshDataWorker: Work request for sync is run
12:16:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:31:45 D/RefreshDataWorker: Work request for sync is run
12:31:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:47:05 D/RefreshDataWorker: Work request for sync is run
12:47:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
13:01:45 D/RefreshDataWorker: Work request for sync is run
13:01:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

ステップ 3: 制約を追加する

このステップでは、PeriodicWorkRequest に次の制約を追加します。

  • バッテリー残量が少ないわけではありません。
  • デバイスの充電中。
  • デバイスがアイドル状態。API レベル 23(Android M)以上でのみ使用できます。

DevByteApplication クラスに以下を実装します。

  1. DevByteApplication クラスの setupRecurringWork() メソッド内で、バッテリー残量が低下していない場合にのみ処理リクエストを実行するように指定します。build() メソッド呼び出しの前に制約を追加し、setRequiresBatteryNotLow() メソッドを使用します。
.setRequiresBatteryNotLow(true)
  1. デバイスが充電中の場合にのみ実行されるように、処理リクエストを更新します。build() メソッド呼び出しの前に制約を追加し、setRequiresCharging() メソッドを使用します。
.setRequiresCharging(true)
  1. デバイスがアイドル状態のときのみ実行されるように、作業リクエストを更新します。build() メソッド呼び出しの前に制約を追加し、setRequiresDeviceIdle() メソッドを使用します。この制約は、ユーザーがデバイスをアクティブに使用していない場合にのみ処理リクエストを実行します。この機能は Android 6.0(Marshmallow)以降でのみ利用できるため、SDK バージョン M 以降の条件を追加します。
.apply {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       setRequiresDeviceIdle(true)
   }
}

constraints オブジェクトの完全な定義は次のとおりです。

val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .setRequiresBatteryNotLow(true)
       .setRequiresCharging(true)
       .apply {
           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
               setRequiresDeviceIdle(true)
           }
       }
       .build()
  1. setupRecurringWork() メソッド内で、リクエスト間隔を 1 日 1 回に戻します。
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .setConstraints(constraints)
       .build()

定期的な処理リクエストがスケジュールされたタイミングをトラッキングできるようにログを伴う、setupRecurringWork() メソッドの完全な実装は次のようになります。

private fun setupRecurringWork() {

       val constraints = Constraints.Builder()
               .setRequiredNetworkType(NetworkType.UNMETERED)
               .setRequiresBatteryNotLow(true)
               .setRequiresCharging(true)
               .apply {
                   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                       setRequiresDeviceIdle(true)
                   }
               }
               .build()
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
               .setConstraints(constraints)
               .build()
       
       Timber.d("Periodic Work request for sync is scheduled")
       WorkManager.getInstance().enqueueUniquePeriodicWork(
               RefreshDataWorker.WORK_NAME,
               ExistingPeriodicWorkPolicy.KEEP,
               repeatingRequest)
   }
  1. 以前にスケジュール設定した作業リクエストを削除するには、デバイスまたはエミュレータから DevBytes アプリをアンインストールします。
  2. アプリを実行すると、WorkManager はすぐに WorkRequest のスケジュールを設定します。処理リクエストは、すべての制約が満たされると 1 日 1 回実行されます。
  3. この作業リクエストは、アプリが実行されていなくても、アプリがインストールされている限りバックグラウンドで実行されます。そのため、スマートフォンからアプリをアンインストールする必要があります。

その調子です。DevBytes アプリで動画を毎日プリフェッチするための、バッテリーに優しい作業リクエストを実装してスケジュール設定しました。WorkManager は、システム リソースを最適化しながら、作業をスケジュール設定して実行します。ユーザーとバッテリーの寿命を延ばすことができます。

Android Studio プロジェクト: DevBytesWorkManager

  • WorkManager API を使用すると、延期可能な(ただし確実に実行される必要がある)非同期タスクのスケジュールを簡単に設定できます。
  • 現実世界のほとんどのアプリでは、長時間実行されるバックグラウンド タスクを実行する必要があります。バックグラウンド タスクを最適化された効率的な方法でスケジュール設定するには、WorkManager を使用します。
  • WorkManager ライブラリのメインクラスは、WorkerWorkRequestWorkManager です。
  • Worker クラスは作業単位を表します。バックグラウンド タスクを実装するには、Worker クラスを拡張して doWork() メソッドをオーバーライドします。
  • WorkRequest クラスは、作業単位の実行リクエストを表します。WorkRequest は、WorkManager でスケジュールする作業のパラメータを指定するための基本クラスです。
  • WorkRequest クラスには、1 回限りのタスク用の OneTimeWorkRequest と、定期的な処理リクエスト用の PeriodicWorkRequest の 2 つの具体的な実装があります。
  • WorkRequest を定義する際は、Worker を実行するタイミングを示す Constraints を指定できます。制約には、デバイスが電源に接続されているかどうか、デバイスがアイドル状態かどうか、Wi-Fi が接続されているかどうかなどが含まれます。
  • WorkRequest に制約を追加するには、Constraints.Builder のドキュメントに記載されている set メソッドを使用します。たとえば、デバイスのバッテリー残量が少ない場合に WorkRequest を実行しないようにするには、setRequiresBatteryNotLow() の set メソッドを使用します。
  • WorkRequest を定義したら、タスクを Android システムに引き渡します。これを行うには、WorkManager enqueue メソッドのいずれかを使用してタスクをスケジュールします。
  • Worker の正確な実行タイミングは、WorkRequest で使用される制約とシステムの最適化に左右されます。WorkManager は、こうした制約の下で最善の動作を行うように設計されています。

Udacity コース:

Android デベロッパー ドキュメント:

その他:

このセクションでは、インストラクター主導のコースの一環として、この Codelab に取り組んでいる生徒向けに考えられる宿題をいくつか示します。インストラクターは、以下のようなことを行えます。

  • 必要に応じて宿題を与える
  • 宿題の提出方法を生徒に伝える
  • 宿題を採点する

インストラクターは、これらの提案を必要なだけ使用し、必要に応じて他の宿題も自由に与えることができます。

この Codelab に独力で取り組む場合は、これらの宿題を自由に使用して知識をテストしてください。

問題 1

WorkRequest クラスの具象実装は何ですか?

OneTimeWorkPeriodicRequest

OneTimeWorkRequestPeriodicWorkRequest

OneTimeWorkRequestRecurringWorkRequest

OneTimeOffWorkRequestRecurringWorkRequest

問題 2

API 23 以降でバックグラウンド タスクをスケジュール設定するために WorkManager が使用するクラスは次のうちどれですか。

JobScheduler のみ

BroadcastReceiverAlarmManager

AlarmManagerJobScheduler

SchedulerBroadcastReceiver

問題 3

WorkRequest に制約を追加するには、どの API を使用しますか?

setConstraints()

addConstraints()

setConstraint()

addConstraintsToWorkRequest()

次のレッスンに進む: 10.1 スタイルとテーマ

このコースの他の Codelab へのリンクについては、Android Kotlin の基礎の Codelab のランディング ページをご覧ください。