一、前言:
WorkManager 是适合用于持久性工作的推荐解决方案。如果工作始终要通过应用重启和系统重新启动来调度,便是持久性的工作。由于大多数后台处理操作都是通过持久性工作完成的,因此 WorkManager 是适用于后台处理操作的主要推荐 API。
1、WorkManager 可处理三种类型的持久性工作:
- 立即执行:必须立即开始且很快就完成的任务,可以加急。
- 长时间运行:运行时间可能较长(有可能超过 10 分钟)的任务。
- 可延期执行:延期开始并且可以定期运行的预定任务。
类型 | 周期 | 使用方式 |
---|---|---|
立即 | 一次性 | OneTimeWorkRequest 和 Worker。如需处理加急工作,请对 OneTimeWorkRequest 调用 setExpedited()。 |
长期运行 | 一次性或定期 | 任意 WorkRequest 或 Worker。在工作器中调用 setForeground() 来处理通知。 |
可延期 | 一次性或定期 | PeriodicWorkRequest 和 Worker。 |
二、兼容范围广
WorkManager最低能兼容API Level 14,并且不需要你的设备安装有Google Play Services。因此,你不用过于担心兼容性问题,因为API Level 14已经能够兼容几乎100%的设备了。
WorkManager能依据设备的情况,选择不同的执行方案。在API Level 23+,通过JobScheduler来完成任务,而在API Level 23以下的设备中,通过AlarmManager和Broadcast Receivers组合完成任务。但无论采用哪种方案,任务最终都是交由Executor来完成。
WorkManager的两个重要特点
1.针对不需要及时完成的任务
比如,发送应用程序日志,同步应用程序数据,备份用户数据等。站在业务的角度,这些任务都不需要立即完成,如果我们自己来管理这些任务,逻辑可能会非常复杂,若API使用不恰当,可能会消耗大量电量。
2.保证任务一定会被执行
WorkManager能保证任务一定会被执行,即使你的应用程序当前不在运行中,哪怕你的设备重启,任务仍然会在适当的时候被执行。这是因为WorkManager有自己的数据库,关于任务的所有信息和数据都保存在这个数据库中,因此,只要你的任务交给了WorkManager,哪怕你的应用程序彻底退出,或者设备重新启动,WorkManager依然能够保证完成你交给的任务。
注意:WorkManager不是一种新的工作线程,它的出现不是为了替代其它类型的工作线程。工作线程通常立即运行,并在执行完成后给到用户反馈。而WorkManager不是即时的,它不能保证任务能立即得到执行。
三、在项目中使用WorkManager
1.在app的build.gradle中添加依赖。
dependencies {
def work_version = "2.8.1"
// (Java only)
implementation "androidx.work:work-runtime:$work_version"
// Kotlin + coroutines
implementation "androidx.work:work-runtime-ktx:$work_version"
}
2.使用Worker定义任务 。
继承Worker类,覆盖doWork()方法,所有需要在任务中执行的代码都在该方法中编写。
class UploadWorker(appContext: Context, workerParams: WorkerParameters):
Worker(appContext, workerParams) {
override fun doWork(): Result {
//获取传递的值
val mBookId = inputData.getString("book_id")
val buyNum = inputData.getString("num")
// 执行一些后台任务....
//返回成功
return Result.success()
}
}
doWork()方法有三种类型的返回值:
- 执行成功返回Result.success()
- 执行失败返回Result.failure()
- 需要重新执行返回Result.retry()
3.普通任务执行
//传输数据
val inputData = Data.Builder()
.putString("book_id", "1234")
.putString("num", "88")
.build()
OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadLogWorker.class)
.setInitialDelay(10, TimeUnit.SECONDS)//延迟10秒执行
.setInputData(inputData) //传输数据
.build();
//开始执行任务
WorkManager.getInstance(this).enqueue(uploadWorkRequest);
设置延迟执行任务。假设你没有设置触发条件,或者当你设置的触发条件符合系统的执行要求,此时,系统有可能立刻执行该任务,但如果你希望能够延迟执行,那么可以通过setInitialDelay()方法,延后任务的执行。
4.使用WorkRequest配置任务
设置任务触发条件。例如,我们可以设置在设备处于充电,
网络已连接,且电池电量充足的状态下,
才出发我们设置的任务。
Constraints constraints = new Constraints.Builder()
.setRequiresCharging(true)
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build();
//设置请求
OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadLogWorker.class)
.setConstraints(constraints)//设置触发条件
.build();
//开始执行任务
WorkManager.getInstance(this).enqueue(uploadWorkRequest);
5.观察任务的状态。
任务在提交给系统后,通过WorkInfo获知任务的状态,WorkInfo包含了任务的id,tag,以及Worker对象传递过来的outputData,以及任务当前的状态。有三种方式可以得到WorkInfo对象。
WorkManager.getWorkInfosByTag()
WorkManager.getWorkInfoById()
WorkManager.getWorkInfosForUniqueWork()
如果你希望能够实时获知任务的状态。这三个方法还有对应的LiveData方法。
WorkManager.getWorkInfosByTagLiveData()
WorkManager.getWorkInfoByIdLiveData()
WorkManager.getWorkInfosForUniqueWorkLiveData()
通过LiveData,我们便可以在任务状态发生变化的时候,收到通知。
WorkManager.getInstance(this).getWorkInfoByIdLiveData(uploadWorkRequest.getId()).observe(MainActivity.this, new Observer<WorkInfo>()
{
@Override
public void onChanged(WorkInfo workInfo)
{
Log.d("onChanged()->", "workInfo:"+workInfo);
}
});
6.取消任务。与观察任务类似的,我们也可以根据Id或者Tag取消某个任务,或者取消所有任务。
WorkManager.getInstance(MainActivity.this).cancelAllWork();
7.WorkManager和Worker之间的参数传递
WorkManager通过setInputData()方法向Worker传递数据。
Data inputData = new Data.Builder()
.putString("input_data", "aaaaa")
.putString("input_data2", "hahah")
.build();
OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadLogWorker.class)
.setInputData(inputData)
.build();
Worker中接收数据,并在任务执行完成后,向WorkManager传递数据。
@Override
public Result doWork()
{
//接收外面传递进来的数据
String inputData1 = getInputData().getString("input_data");
String inputData2 = getInputData().getString("input_data2");
// 任务执行完成后返回数据
Data outputData = new Data.Builder()
.putString("output_data", "Task Success!")
.build();
return Result.success(outputData);
}
WorkManager通过LiveData的WorkInfo.getOutputData(),得到从Worker传递过来的数据。
WorkManager.getInstance(this).getWorkInfoByIdLiveData(uploadWorkRequest.getId()).observe(MainActivity.this, new Observer<WorkInfo>()
{
@Override
public void onChanged(WorkInfo workInfo)
{
if (workInfo != null && workInfo.getState() == WorkInfo.State.SUCCEEDED)
{
String outputData = workInfo.getOutputData().getString("output_data");
}
}
});
注意:Data只能用于传递一些小的基本类型数据,且数据最大不能超过10kb。
8.周期任务PeriodicWorkRequest。前面提到过,WorkRequest的两种实现OneTimeWorkRequest和PeriodicWorkRequest,分别对应的是一次性任务和周期性任务。一次性任务,即任务在成功完成后,便彻底结束。而周期性任务则会按照设定的时间定期执行。二者使用起来没有太大差别。
需要注意的是:周期性任务的间隔时间不能小于15分钟。
PeriodicWorkRequest uploadWorkRequest = new PeriodicWorkRequest.Builder(UploadLogWorker.class, 15, TimeUnit.MINUTES)
.setConstraints(constraints)
.addTag(TAG)
.build();
9.任务链。如果你有一系列的任务需要顺序执行,那么可以利用WorkManager.beginWith().then().then()...enqueue()方法。例如:我们在上传数据之前,需要先对数据进行压缩。
WorkManager.getInstance(this).beginWith(compressWorkRequest).then(uploadWorkRequest).enqueue();
假设在上传数据之前,除了压缩数据,还需要更新本地数据。压缩与更新本地数据二者没有顺序,但与上传数据存在先后顺序。
WorkManager.getInstance(this).beginWith(compressWorkRequest, updateLocalWorkRequest).then(uploadWorkRequest).enqueue();
假设有更复杂的任务链,你还可以考虑使用WorkContinuation.combine()方法,将任务链组合起来。
WorkContinuation workContinuation1 = WorkManager.getInstance(this).beginWith(WorkRequestA).then(WorkRequestB);
WorkContinuation workContinuation2 = WorkManager.getInstance(this).beginWith(WorkRequestC).then(WorkRequestD);
List<WorkContinuation> taskList = new ArrayList<>();
taskList.add(workContinuation1);
taskList.add(workContinuation2);
WorkContinuation.combine(taskList).then(WorkRequestE).enqueue();