在前面的文章中,我已经结合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状态。
SUCCEEDED、FAILED和CANCELLED都表示此工作的终止状态,并且WorkInfo.State.isFinished()都返回true。
重复工作的状态
对于PeriodicWorkRequest来说,只有一个终止状态CANCELLED。SUCCEEDED和FAILED仅仅适用于one-time工作。这是因为定期工作除了取消工作之外,永远都不会结束。每次运行结束之后,无论结果如何,系统都会重新对其调度。
工作链
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)