Android Jetpackシリーズ編(二)WorkManager


この記事では、Androidバックグラウンドタスクを柔軟に管理できるJetpackアーキテクチャコンポーネントのWorkManagerについて説明します.
主な特徴
  • 後方互換API 14は、API 23+のデバイスにJobScheduler、API 14-22を有するデバイスにBroadcastReceiver+AlarmManagerの組合せ
  • を用いる
  • は、ネットワーク状態や課金状態等の制約条件
  • を設定することができる.
  • 非同期1回または定期タスク
  • を実現
  • 計画タスクの有効な監視と管理
  • は、複数のタスクの並列またはシリアル実行
  • を実現する.
  • アプリケーションまたはデバイスの再起動により、タスクが
  • を実行することも保証される.
  • Dozeモード等の省電力機能
  • を保持する.
  • は、バックエンドサービス
  • にログまたは分析を送信することができる.
  • は、アプリケーションデータを定期的にサーバに同期することができる
  • .
    コンポーネント
    主に、Worker、WorkRequest、Constraints、WorkManager、WorkContinuationから構成されています.
    Workerは、バックグラウンドの時間のかかる操作に専念し、パラメータを柔軟に受信し、結果を返すタスク処理オブジェクトです.
    抽象クラスでもあり、抽象メソッドdoWork()があり、すべての論理時間のかかる操作はこのメソッドで実行できます.実行に成功すればResult.success()に戻り、失敗すればResult.failure()に戻り、再試行すればResult.retry()に戻り、結果を返すときに成功または失敗の中で渡すことができます.例えば、Result.success(data)です.
    WorkRequestタスクリクエストオブジェクト
    同様に抽象クラスであり、システムは私たちに2つの実装クラス、OneTimeWorkRequestPeriodicWorkRequestを提供し、前者は1回のタスクであり、後者は循環タスクである.実行するタスクBuilder()を手配し、実行条件setConstraints()を設定し、タスクに必要なパラメータsetInputData()を渡すことができる
    Constraintsタスク実行条件オブジェクト
    設定ネットワーク状態setRequiredNetworkType()設定電力量が低いsetRequiresBatteryNotLow()設定充電時にsetRequiresCharging()を実行するかどうか設定記憶容量が不足しているsetRequiresStorageNotLow()を実行するかどうか
    WorkManagerタスク管理オブジェクトで、タスクの事前開始、リスニング、キャンセルなどの操作が可能
    事前開始タスク操作beginWith()タスクLiveDataオブジェクトgetWorkInfoByIdLiveData()キャンセルcancelWorkById()WorkContinuationタスクの継続オブジェクト.このオブジェクトを使用して、タスクの組合せを実行できます.
    接続タスクアクションthen()並列接続アクションcombine()実行アクションenqueue()単純な使用CompressWorkerを作成し、一言だけ印刷して成功に戻ります.
    class CompressWorker(context : Context, params : WorkerParameters): Worker(context, params) {
    
        override fun doWork(): Result {
            Logger.d("doWork....")
            // Indicate success or failure with your return value:
            return Result.success()
        }
    
    }
    

    タスク実行条件の指定
    val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)  //     
            .setRequiresBatteryNotLow(true)                 //          
            .setRequiresCharging(true)                      //       
            .setRequiresStorageNotLow(true)                 //            
            .setRequiresDeviceIdle(true)                    //         ,   API 23
            .build()
    

    タスクの作成
    //          
    val workA = OneTimeWorkRequest.Builder(CompressWorker::class.java).setConstraints(constraints).build()
    //          
    val workB = OneTimeWorkRequest.Builder(MathWorker::class.java)
                    .setInputData(Data.Builder()
                            .putInt(MathWorker.KEY_X_ARG, 1)
                            .putInt(MathWorker.KEY_Y_ARG, 2)
                            .putInt(MathWorker.KEY_Z_ARG, 3)
                            .build())
                    .build()
    //        30      ,      15  ,  JobScheduler API     
    val workC = PeriodicWorkRequest.Builder(CompressWorker::class.java, 30, TimeUnit.MINUTES).build()
    
    MathWorkerクラス
    class MathWorker(context : Context, params : WorkerParameters) : Worker(context, params) {
    
        companion object {
            // Define the parameter keys:
            const val KEY_X_ARG = "X"
            const val KEY_Y_ARG = "Y"
            const val KEY_Z_ARG = "Z"
            // ...and the result key:
            const val KEY_RESULT = "result"
        }
    
        override fun doWork(): Result {
            val x = inputData.getInt(KEY_X_ARG, 0)
            val y = inputData.getInt(KEY_Y_ARG, 0)
            val z = inputData.getInt(KEY_Z_ARG, 0)
            // ...do the math...
            val result = myCrazyMathFunction(x, y, z)
            //...set the output, and we're done!
            val output: Data = Data.Builder().putInt(KEY_RESULT, result).build()
    
            return Result.success(output)
        }
    
        private fun myCrazyMathFunction(x: Int, y: Int, z: Int): Int{
            return x + y + z
        }
    }
    

    リスニングタスク
    WorkManager.getInstance().getWorkInfoByIdLiveData(workA.id)
            .observe({ lifecycle }, { workInfo ->
                // Do something with the status
                if (workInfo != null && workInfo.state.isFinished) {
                    Logger.d("workA")
                }
            })
    WorkManager.getInstance().getWorkInfoByIdLiveData(workB.id)
            .observe({ lifecycle }, { workInfo ->
                // Do something with the status
                if (workInfo != null && workInfo.state.isFinished) {
                    //   MathWorker      
                    Logger.d("workE="+workInfo.outputData.keyValueMap[MathWorker.KEY_RESULT])
                }
            })
    WorkManager.getInstance().getWorkInfoByIdLiveData(workC.id)
            .observe({ lifecycle }, { workInfo ->
                // Do something with the status
                Logger.d("workInfo="+workInfo?.state)
                if (workInfo != null && workInfo.state.isFinished) {
                    Logger.d("workInfo="+workInfo.id)
                }
            })
    

    タスクの実行
    //      
    WorkManager.getInstance().enqueue(workA)
    
    //      
    WorkManager.getInstance().beginWith(workA).then(workB).enqueue()
    
    //workA workB     ,   workC
    WorkManager.getInstance().beginWith(listOf(workA, workB)).then(workC).enqueue()
    
    //workA workB  ,workC workD  ,chain1 chain2  
    val chain1 = WorkManager.getInstance().beginWith(workA).then(workB)
    val chain2 = WorkManager.getInstance().beginWith(workC).then(workD)
    WorkContinuation.combine(listOf(chain1, chain2)).enqueue()
    

    タスクのキャンセル
    WorkManager.getInstance().cancelWorkById(workA.id)
    

    WorkMangerの紹介はここまでです.詳細は、公式ドキュメントを参照してください.https://developer.android.com/topic/libraries/architecture/workmanager