WorkManager - Ki-Kobayashi/Android-Wiki GitHub Wiki

🟩 WorkManagerとは

https://developer.android.com/topic/libraries/architecture/workmanager/basics?hl=ja WorkManagerとは、

  • 「時間がかかる処理」や「永続的な処理」などを、非同期でバックグラウンドで実行できる

  • アプリを閉じている時にも実行できる

  • タスクをスケジュールし、以下の3つの操作が可能になる
    https://developer.android.com/topic/libraries/architecture/workmanager?hl=ja#types

    • 即時(1回限り)
    • 長時間実行(1回限り or 定期的)
    • 設定したスケジュール、条件を満たしたとき実行させられる(1回限り or 定期的)
  • スケジュールされた処理は、内部のSQLite データベースに保存される

  • Doze モードなどの省電力機能やベスト プラクティスに従うため、電力消費を気にする必要なし

.

🟡 条件とは

以下のような条件を満たしたときだけ実行するときにも、WorkManagerは使える

  • 「Wifiに繋いでる時だけ」「保存容量が○○GB以上の時」「充電○%以上」のような条件を指定してその条件になるまで待ってから実行
  • 並列実行と順次実行

参考:https://developer.android.com/codelabs/basic-android-kotlin-compose-workmanager?hl=ja&continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fandroid-basics-compose-unit-7-pathway-1%3Fhl%3Dja%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fbasic-android-kotlin-compose-workmanager#3

🟡 具体例

在庫確認に使用するアプリの場合、在庫数の最新の値を定期的に取得する必要がある

.

🟩 APIレベルが23未満の場合

WorkManagerではなく、「BroadcastReceiver」+「AlarmManager」の組み合わせを利用する必要がある

.

🟩 WorkManagerの作成手順

  1. WorkerManagerを導入する
  2. Worker のサブクラス作成
  3. Work リクエストの作成 + 実行

🟡1:WorkManagerの導入

https://developer.android.com/topic/libraries/architecture/workmanager/basics?hl=ja

💎app / build.gradle

dependencies {
    val work_version = "2.8.0"
    // Kotlin + coroutines
    implementation("androidx.work:work-runtime-ktx:$work_version")

.

🟡2:WorkManagerのサブクラスの作成

https://developer.android.com/topic/libraries/architecture/workmanager/basics?hl=ja#define_the_work

💎任意のクラス名で作成

import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters

class UploadWorker(context: Context, params: WorkerParameters): Worker(context, params) {
    override fun doWork(): Result {
        return try {
      // 💡ここで定期/条件マッチ時などに実行させたい処理をかく
            for (i in 0..60) {
                Log.d("AAAA", "uploading... $i")
            }
            Result.success()
        } catch (e: Exception) {
            Result.failure() 👈忘れずに
        }
    }
}

.

🟡3: Work リクエストの作成 + 実行

https://developer.android.com/topic/libraries/architecture/workmanager/basics?hl=ja#create_a_workrequest

.

💎 1回限り or 定期的

リクエスト作成時は、以下を使い分ける

.

💎設定したいクラスで作成(書き方2種)

📚 1つ目の書き方

    private fun setOneTimeWorkRequest() {
        // リクエストの作成
        val uploadWorkRequest: WorkRequest = OneTimeWorkRequestBuilder<UploadWorker>().build() 👈 build()忘れずに!
        // WorkRequest を WorkManager に送信する
        WorkManager.getInstance(applicationContext).enqueue(uploadWorkRequest)
    }

.
📚 2つ目の書き方

    private fun setOneTimeWorkRequest() {    
        // リクエストの作成    
        val uploadWorkRequest = OneTimeWorkRequest.Builder(UploadWorker::class.java).build()👈 build()忘れずに!
        // WorkRequest を WorkManager に送信する
        WorkManager.getInstance(applicationContext).enqueue(uploadWorkRequest)
    }

.

💎WorkManagerの実行制約(条件)を付ける方法

🛑ネット状況を確認するには、マニフェストファイルに下記二つの権限を追加しておく

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

.
📚 WorkManagerの実行制約(条件)付きリクエストの作成

private fun setOneTimeWorkRequest() {
        val workManager = WorkManager.getInstance(applicationContext)
        // 💡手順1:実行条件(制約)の設定
        val constraints = Constraints.Builder()
            .setRequiresCharging(true) // 💡充電中である
            .setRequiredNetworkType(NetworkType.CONNECTED) // 💡ネット接続中である
            .build()

        val uploadWorkRequest: WorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
            .setConstraints(constraints) // 💡手順2:リクエストにセット
            .build()
        // 💡タスクの実行
        workManager.enqueue(uploadWorkRequest)

        // WorkManagerの状態を監視する
        workManager.getWorkInfoByIdLiveData(uploadWorkRequest.id)
            .observe(this, Observer {
                binding.textWork.text = it.state.name
            })
}

.

💎WorkRequestで設定できるもの

https://developer.android.com/topic/libraries/architecture/workmanager/how-to/define-work?hl=ja

  • 1回限りの処理と繰り返し処理のスケジュールを設定する
  • 処理の制約を設定する(Wi-Fi や充電の必要など)
  • 処理の実行の遅延が最小限になるよう保証する
  • 再試行とバックオフの戦略を設定する
  • 入力データを処理に渡す
  • タグを使用して関連する処理をグループ化する

.

🟡2-2:WorkManagerの状態を監視して取得

💡コード内の「this」は、ライフサイクルオーナー(Activityの場合はthisを使う)

// WorkManagerの状態を監視する
workManager.getWorkInfoByIdLiveData(uploadWorkRequest.id)
   .observe(this, Observer {
       binding.textWork.text = it.state.name
    })

.

💎WorkInfoで得られる状態

  • Blocked: まだ作業できない状態
  • Enqueued:実行可能状態
  • Running:実行中
  • Succeed:作業成功

.

🟩 Workerのサブクラスに値を渡す方法

https://developer.android.com/topic/libraries/architecture/workmanager/how-to/define-work?hl=ja

Workerのサブクラスに値を渡すには、WorkerManagerのインスタンスを作成する箇所と、受け手であるサブクラスで設定が必要

以下の2種によって、設定方法が異なる

  • 🟡 呼び出し側 から WorkManager(入力データを渡す)
  • 🟣 WorkManager から 呼び出し側 (出力データを返す)

.

🟡【呼び出し側 から WorkManager】送り手の設定

「💎」箇所を参照

private fun setOneTimeWorkRequest() {
        val workManager = WorkManager.getInstance(applicationContext)
        // 💎:WorkerManagerに渡したい値を生成(※Data: workパッケージのもの)
        val data: Data = Data.Builder()
            .putInt(KEY_COUNT, 250)
            .build()
        // 🌟:実行条件(制約)の設定
        val constraints = Constraints.Builder()
            .setRequiresCharging(true) // 充電中である
            .setRequiredNetworkType(NetworkType.CONNECTED) // ネット接続中である
            .build()

        val uploadWorkRequest: WorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
            .setInputData(data) // 💎:WorkerManager渡したいデータのセット(WorkerManagerサブクラスで受け取る処理も追加すること)
            .setConstraints(constraints) // 🌟:リクエストにセット
            .build()
        // タスクの実行
        workManager.enqueue(uploadWorkRequest)
        // WorkManagerの状態を監視する
        workManager.getWorkInfoByIdLiveData(uploadWorkRequest.id)
            .observe(this, Observer {
                binding.textWork.text = it.state.name
            })
    }

    companion object {
        const val KEY_COUNT = "key_count"
    }

.

🟡【呼び出し側 から WorkManager】受け手の設定

import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters

class UploadWorker(context: Context, params: WorkerParameters): Worker(context, params) {
    override fun doWork(): Result {
        return try {
            // 💎:WorkManagerのインスタンス生成箇所から、データを受け取る
            val count = inputData.getInt(MainActivity.KEY_COUNT, 0)

            for (i in 0..count) {
                Log.d("AAAA", "uploading... $i")
            }
            Result.success()
        } catch (e: Exception) {
            Result.failure()
        }
    }
}

.

🟣【WorkManager から 呼び出し側】送り手の設定

「🟣」箇所を参照

import android.content.Context
import android.util.Log
import androidx.work.Data
import androidx.work.Worker
import androidx.work.WorkerParameters
import java.text.SimpleDateFormat
import java.util.Date

class UploadWorker(context: Context, params: WorkerParameters): Worker(context, params) {
    override fun doWork(): Result {
        return try {
            // 💎:WorkManagerのインスタンス生成箇所から、データを受け取る
            val count = inputData.getInt(MainActivity.KEY_COUNT, 0)

            for (i in 0..count) {
                Log.d("AAAA", "uploading... $i")
            }

            // 🟣処理完了時刻を呼び出し側に返す
            val sdf = SimpleDateFormat("yyyy/M/dd hh:mm:ss")
            val currentDateTime = sdf.format(Date())
            // 🟣返却データの生成
            val outPutData = Data.Builder()
                .putString(KEY_WORKER, currentDateTime)
                .build()
            Result.success(outPutData) // 🟣succeess にセットして返す(呼び出し元にも、受け取る処理を追加すること)
        } catch (e: Exception) {
            Result.failure()
        }
    }

    companion object{
        const val KEY_WORKER = "key_worker"
    }
}


.

🟣【WorkManager から 呼び出し側】受け手の設定

「🟣」箇所を参照

    // WorkManagerの状態を監視する
        workManager.getWorkInfoByIdLiveData(uploadWorkRequest.id)
            .observe(this, Observer {
                binding.textWork.text = it.state.name
                //🟣WorkManager側からの返却値はここで受け取る + Toast表示
                //🟣処理が完了していないと、データもないため、必ずチェックする
                if (it.state.isFinished) {
                    val message = it.outputData.getString(UploadWorker.KEY_WORKER)
                    Toast.makeText(applicationContext, message, Toast.LENGTH_SHORT)
                }
            })

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.