WorkManager of Jetpack's Architecture Components

介绍

WorkManager 旨在用于可延迟运行(即不需要立即运行)并且在应用退出或设备重启时必须能够可靠运行的任务。例如:

  • 向后端服务发送日志或分析数据
  • 定期将应用数据与服务器同步

步骤

  1. 将 WorkManager 添加到您的 Android 项目中

    将以下依赖项添加到应用的 build.gradle 文件:

dependencies {
  def work_version = "2.4.0"

    // (Java only)
    implementation "androidx.work:work-runtime:$work_version"

    // Kotlin + coroutines
    implementation "androidx.work:work-runtime-ktx:$work_version"

    // optional - RxJava2 support
    implementation "androidx.work:work-rxjava2:$work_version"

    // optional - GCMNetworkManager support
    implementation "androidx.work:work-gcm:$work_version"

    // optional - Test helpers
    androidTestImplementation "androidx.work:work-testing:$work_version"
  }

2.创建后台任务Worker

class DialogAppraiseTipsWorker(val context: Context, parms: WorkerParameters) : Worker(context, parms) {

    override fun doWork(): Result {
        return if (isNeedShowDialog) {
            logD(TAG, "${System.currentTimeMillis()},execute doWork function,result : retry")
            Result.retry()
        } else {
            logD(TAG, "${System.currentTimeMillis()},execute doWork function,result : success")
            Result.success()
        }
    }
}

自定义任务类,重写dowork方法,从 doWork() 返回的 Result 会通知 WorkManager 任务:

  • 已成功完成:Result.success()
  • 已失败:Result.failure()
  • 需要稍后重试:Result.retry()

3.配置WorkRequest,包装运行任务的方式和时间

任务可以是一次性的,也可以是周期性的。对于一次性 WorkRequest,请使用 OneTimeWorkRequest,对于周期性工作,请使用 PeriodicWorkRequestWorkRequest可以包含任务在运行时应遵循的约束、工作输入、延迟,以及重试工作的退避时间策略

val showDialogWorkRequest = OneTimeWorkRequest.Builder(DialogAppraiseTipsWorker::class.java)
    .build()

4.将任务提交给系统

WorkManager.getInstance(this).enqueue(DialogAppraiseTipsWorker.showDialogWorkRequest)

WorkRequest

上面步骤中讲了可以通过WorkRequest来配置任务的运行方式和时间等信息。

任务添加约束

    // Create a Constraints object that defines when the task should run
    val constraints = Constraints.Builder()
            .setRequiresDeviceIdle(true)
            .setRequiresCharging(true)
            .build()

    // ...then create a OneTimeWorkRequest that uses those constraints
    val showDialogWorkRequest = OneTimeWorkRequest.Builder(DialogAppraiseTipsWorker::class.java)
            .setConstraints(constraints)
            .build()


任务将仅在满足所有约束时才会运行,如果加了多个约束必须全部满足。

初始延迟

val showDialogWorkRequest = OneTimeWorkRequest.Builder(DialogAppraiseTipsWorker::class.java)
         .setInitialDelay(10, TimeUnit.MINUTES)
        .build()

设置任务10分钟后执行,默认立即执行

添加标记

添加一个Tag标记,用于后期来通过Tag取消任务和获取任务状态

val showDialogWorkRequest = OneTimeWorkRequest.Builder(DialogAppraiseTipsWorker::class.java)
    .addTag(TAG)
    .setInitialDelay(1, TimeUnit.MINUTES)
    .build()

重试退避策略

val showDialogWorkRequest = OneTimeWorkRequest.Builder(DialogAppraiseTipsWorker::class.java)
    .addTag(TAG)
    .setInitialDelay(1, TimeUnit.MINUTES)
    .setBackoffCriteria(BackoffPolicy.LINEAR, 5, TimeUnit.SECONDS)
    .build()

设定重试任务前的等待时间,最小10秒,最大5小时。BackoffPolicy有两个值LINEAR(每次重试的时间线性增加,比如第一次10分钟,第二次就是20分钟)、EXPONENTIAL(每次重试时间指数增加)。

任务输入输出

Worker父类会有getInputDatasetOutputData方法来输入和输出DataData里有个Map<String, Object> mValues变量用来存放键值对信息。而Worker的getInputData获取的Data是从WorkRequest中的setInputData方法得来的。

所以setInputData是给Worker传输入参数的

    Data imageData = new Data.Builder()
                    .putString(Constants.KEY_IMAGE_URI, imageUriString)
                    .build();

    OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadWorker.class)
            .setInputData(imageData)
            .build();

在Worker中输出Data类型返回值,添加到Result中

    public class UploadWorker extends Worker {

        public UploadWorker(
            @NonNull Context context,
            @NonNull WorkerParameters params) {
            super(context, params);
        }

        @Override
        public Result doWork() {

            // Get the input
            String imageUriInput =
                    getInputData().getString(Constants.KEY_IMAGE_URI);
            // TODO: validate inputs.
            // Do the work
            Response response = uploadFile(imageUriInput);

            // Create the output of the work
            Data outputData = new Data.Builder
                    .putString(Constants.KEY_IMAGE_URL, response.imageUrl)
                    .build();

            // Return the output
            return Result.success(outputData);
        }
    }

观察工作状态

观察WorkInfo状态信息,满足条件后向用户显示信息。

WorkManager.getInstance(this).getWorkInfoByIdLiveData(DialogAppraiseTipsWorker.showDialogWorkRequest.id).observe(this, Observer { workInfo ->
    logD(TAG, "observe: workInfo.state = ${workInfo.state}")
    if (workInfo.state == WorkInfo.State.SUCCEEDED) {
        WorkManager.getInstance(this).cancelAllWork()
    } else if (workInfo.state == WorkInfo.State.RUNNING) {
        if (isActive) {
            DialogAppraiseTipsWorker.showDialog(this)
            WorkManager.getInstance(this).cancelAllWork()
        }
    }
})

这里我是通过WorkManager.getWorkInfoByIdLiveData(UUID)来获取了LiveData<WorkInfo>,还支持WorkManager.getWorkInfosByTagLiveData(String))通过Tag来检索,或者WorkManager.getWorkInfosForUniqueWorkLiveData(String)) 通过唯一任务名称来检索都可以。

------ 本文结束 ------
坚持原创技术分享,您的支持将鼓励我继续创作!