WorkManager总结

在前面的文章中,我已经结合Android Developer上的文章和自己的实例,详细的讲解的WorkManager的方方面面。

在本篇文章中,我将对WorkManager进行一个总结。

什么时候推荐使用WorkManager

①延时任务。WorkManager允许对Worker设置一个延时,所以可以非常方便的执行延时任务。

②及时任务。对于短时间的任务,我们可以使用其他的方法来执行。但是对于长时间,并且当应用处于后台或者重启之后还执行的任务,推荐使用WorkManager。

※WorkManager支持的最低的系统是API 14(Android 4.0)。

Worker

对于WorkManager来说,Worker是一个非常重要的类。我们执行的任务就是定义在Worker中。我们执行延时、约束等也是对于Worker的操作。

Worker的从创建

导入库


dependencies {
    def work_version = "2.5.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"

    // optional - Multiprocess support
    implementation "androidx.work:work-multiprocess:$work_version"
}
复制代码

定义工作

扩展 Worker 类并替换 doWork() 方法。

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

   @Override
   public Result doWork() {

     // Do the work here
     XXXXXXXXXX

     // Indicate whether the work finished successfully with the Result
     return Result.success();
   }
}

复制代码

WorkRequest

上面我们知道了定义一个Worker是怎么实现的了,那么延时,约束是如何实现的呢?这边我们就需要了解WorkRequest这个类了。

WorkRequest可以帮助我们对Worker进行延时、约束、定义一次或者重复性Worker。

一次性Worker-OneTimeWorkRequest

对于无需额外配置的简单工作,请使用静态方法 from

WorkRequest uploadWorkRequest = OneTimeWorkRequest.from(UploadWorker.class);
复制代码

对于更复杂的工作,可以使用构建器。

WorkRequest uploadWorkRequest =
   new OneTimeWorkRequest.Builder(UploadWorker.class)
       // Additional configuration
       .build();
复制代码

重复性Worker-PeriodicWorkRequest

您的应用有时可能需要定期、重复运行某些工作。例如,您可能要定期备份数据、定期下载应用中的新鲜内容或者定期上传日志到服务器。

PeriodicWorkRequest saveRequest =
       new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class, 1, TimeUnit.HOURS) //工作的运行时间间隔定为一小时
           // Constraints
           .build();
复制代码

※ 可以定义的最短重复间隔是 15 分钟。

延时

下面举例说明了如何将工作设置为在加入队列后至少经过 10 分钟后再运行。

WorkRequest myWorkRequest =
      new OneTimeWorkRequest.Builder(MyWork.class)
               .setInitialDelay(10, TimeUnit.MINUTES)
               .build();
复制代码

该示例说明了如何为 OneTimeWorkRequest 设置初始延迟时间,您也可以为 PeriodicWorkRequest 设置初始延迟时间。在这种情况下,定期工作只有首次运行时会延迟。

约束

约束可确保将工作延迟到满足最佳条件时运行。以下约束适用于 WorkManager。

NETWORKTYPE 约束运行工作所需的网络类型。例如 WI-FI (UNMETERED)。
BatteryNotLow 如果设置为 true,那么当设备处于“电量不足模式”时,工作不会运行。
RequiresCharging 如果设置为 true,那么工作只能在设备充电时运行。
DeviceIdle 如果设置为 true,则要求用户的设备必须处于空闲状态,才能运行工作。如果您要运行批量操作,否则可能会降低用户设备上正在积极运行的其他应用的性能,建议您使用此约束。
StorageNotLow 如果设置为 true,那么当用户设备上的存储空间不足时,工作不会运行。

创建一组约束并将其与某项工作相关联,请使用一个 Contraints.Builder() 创建 Constraints 实例,并将该实例分配给 WorkRequest.Builder()

例如,以下代码会构建了一个工作请求,该工作请求仅在用户设备正在充电且连接到 Wi-Fi 网络时才会运行:

Constraints constraints = new Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)  //设置连接到WiFi
       .setRequiresCharging(true)  //设置正在充电
       .build();
​
WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
               .setConstraints(constraints)
               .build();
复制代码

如果指定了多个约束,工作将仅在满足所有约束时才会运行。

如果在工作运行时不再满足某个约束,WorkManager 将停止工作器。系统将在满足所有约束后重试工作。

Worker提交系统

使用 enqueue() 方法将 WorkRequest 提交到 WorkManager

WorkManager
    .getInstance(myContext)
    .enqueue(uploadWorkRequest);
复制代码

唯一Worker

当把工作加入队列的最简单的方法就是调用enqueue()方法,但是这种情况下,有可能将同一个作业多次加入到队列中,所以你可以通过创建一个唯一工作序列,确保同一时刻只有一个具有特定名称的工作实例。

与ID不同,唯一名称是开发者指定的,而不是由WorkManager自动生成的;与Tag也不同,唯一名称仅与一个工作实例相关联。

创建唯一工作的方法:

•WorkManager.enqueueUniqueWork(用于一次性工作)

•WorkManager.enqueueUniquePeriodicWork(用于定期工作)

这两种方法都有3个参数:

•uniqueWorkName - 用于标识唯一工作请求的String。

•existingWorkPolicy - 用于如果已有使用该名称且尚未完成的唯一工作(链),应该执行什么操作。参照冲突解决政策。

•workRequest - 需要调度的WorkRequest。

WorkManager.getInstance(this).enqueueUniquePeriodicWork(
     "sendLogs",
     ExistingPeriodicWorkPolicy.KEEP,
     sendLogsWorkRequest);

复制代码

冲突解决政策

调度唯一工作时,您必须告知 WorkManager 在发生冲突时要执行的操作。您可以通过在将工作加入队列时传递一个枚举来实现此目的。

对于一次性工作,您需要提供一个ExistingWorkPolicy,它有4个选项。

•REPLACE: 用新工作替换现有工作。此选项将取消现有工作。

•KEEP: 保留现有工作,并忽略新工作。

•APPEND: 将新工作附加到现有工作的末尾。此选项会将新工作链接到现有工作,并在现有工作完成后运行。并且现有工作将成为新工作的先决条件。如果现有工作为CANCELLED或者FAILED状态,新工作也会变为CANCELLED或者FAILED状态。

•APPEND_OR_REPLACE: 此选项类似于APPEND,不过它不依赖于现有工作的状态。即使现有工作为CANCELLED或者FAILED状态,新工作仍旧会运行。

对于定期工作,需要提供ExistingPeriodicWorkpolicy,它支持REPLACE和KEEP两个选项。这些选项的功能和对应的ExistingWorkPolicy功能相同。

Worker交互

输入输出

在输入和输出中,输入值以键值对的形式存储在 Data 对象中,并且可以在工作请求中设置。WorkManager 会在执行工作时将输入 Data传递给工作。Worker 类可通过调用 Worker.getInputData() 访问输入参数。

        String[] strings = {"AAA","BBB","CCC","DDD"};
        Data data = new Data.Builder().putStringArray("STRING_ARRAY_KEY", strings).build();
        WorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadWorker.class)
                .setInputData(data)  //输入
                .build();
​
        WorkManager.getInstance(getBaseContext()).enqueue(uploadWorkRequest);
​
        WorkManager.getInstance(getBaseContext()).getWorkInfoByIdLiveData(uploadWorkRequest.getId())
                .observe(this, info -> {
                    if (info != null && info.getState() == WorkInfo.State.SUCCEEDED) {
                        String[] myResult = info.getOutputData().getStringArray("STRING_ARRAY_KEY_OUT");  //输出
                        // ... do something with the result ...
                        String string = "";
                        Log.d("test","myResult is " + myResult.toString());
                        for (int i = 0; i < myResult.length; i++) {
                            string = string + myResult[i] + "\n";
                        }
                        textView.setText(string);
                    }
                });
复制代码

※ 通过Data这种方式的输入输出只适用于OneTimeWorkRequest。对于不适用于PeriodicWorkRequest的原因请参照:WorkManager :创建&提交&输入输出&取消 - 掘金 (juejin.cn)

监视

在将工作加入队列后,你可以额随时按其name、id或者与其关联的tag在WorkManager中进行查询,和检查其状态。

// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>
​
// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>
​
// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>
复制代码
复制代码

该查询会返回WorkInfo对象的ListenableFuture,该值包含工作的id、标记、当前的state和通过Result.success(outputData)设置的任何输出数据。

Worker的取消

在WorkManager中存在如下的四个cancel方法:cancelAllWork、cancelAllWorkByTag、cancelWorkById 和 cancelUniqueWork。

cancelAllWork()

该函数将cancel所有的没有finish的work。

cancelAllWorkByTag(@NonNull String tag)

该函数将cancel所有的没有finish的并且tag符合给定的work。

cancelWorkById(@NonNull UUID id)

该函数将cancel没有finish的并且id符合给定的work。

cancelWorkById(@NonNull UUID id)

该函数将cancel没有finish的并且id符合给定的work。

Worker的状态

一次性工作的状态

对于one-time工作请求,工作的初始状态为ENQUEUED。

在ENQUEUED状态下,你的工作会在满足工作约束和工作延迟的要求后立即执行。然后工作会转为RUNNING状态,再根据工作的结果转为SUCCEEDED、FAILED状态;或者,如果结果是retry,它可能会回到ENQUEUED状态。在这个过程中,你可以随时取消工作,取消后工作将进入CANCELLED状态。

one-time-work-flow.png

SUCCEEDED、FAILED和CANCELLED都表示此工作的终止状态,并且WorkInfo.State.isFinished()都返回true。

重复工作的状态

对于PeriodicWorkRequest来说,只有一个终止状态CANCELLED。SUCCEEDED和FAILED仅仅适用于one-time工作。这是因为定期工作除了取消工作之外,永远都不会结束。每次运行结束之后,无论结果如何,系统都会重新对其调度。

periodic-work-states.png

工作链

WorkManager允许定义多个Worker,然后组成一个链式结构进行运行。 最简单的工作链只需要通过beginWith(worker/arraylist<worker>), then(worker/arraylist<worker>)两个简单的方法就可以实现。 例如:

WorkManager.getInstance(myContext)
    // First, run all the A tasks (in parallel):
    .beginWith(Arrays.asList(workA1, workA2, workA3))
    // ...when all A tasks are finished, run the single B task:
    .then(workB)
    // ...then run the C tasks (in parallel):
    .then(Arrays.asList(workC1, workC2))
    .enqueue();

复制代码

对于复杂一点的工作链,可以通过WorkContinuation来定义多个子链,然后再组成一个父链来提交系统执行。

WorkContinuation chain1 = WorkManager.getInstance(myContext)
    .beginWith(workA)
    .then(workB);
WorkContinuation chain2 = WorkManager.getInstance(myContext)
    .beginWith(workC)
    .then(workD);
WorkContinuation chain3 = WorkContinuation
    .combine(Arrays.asList(chain1, chain2))
    .then(workE);
chain3.enqueue();

复制代码

※ WorkContinuation定义的子链不能保证执行的先后顺序,只能保证子链中的worker是按顺序执行的。

=============================

上面就是我总结的对于WorkManager比较重要的相关内容。如果关于某一块内容还是看不懂,请到下面的文章中看详细的讲解,在这些讲解中我通过实例进行了相关讲解。

WorkManager :后台任务&WorkManager概述 - 掘金 (juejin.cn)

WorkManager :创建&提交&输入输出&取消 - 掘金 (juejin.cn)

WorkManager :唯一Worker & 监视 - 掘金 (juejin.cn)

WorkManager :工作链 - 掘金 (juejin.cn)

WorkManager :工作约束 & 延迟 & 重试 - 掘金 (juejin.cn)

WorkManager : 自定义WorkManager - 掘金 (juejin.cn)

Guess you like

Origin juejin.im/post/7034721918502567973