一、定义:
作为 Android Jetpack 中的新组件,WorkManager 负责用来管理后台任务,说简单点则和异步任务Task或者 Service 作用一样,都可以处理异步任务或后台任务。
核心类介绍
先来介绍下WorkManager中涉及到的想关类
Worker
任务的执行者,是一个抽象类,需要继承它实现要执行的任务。WorkRequest
指定让哪个 Woker 执行任务,指定执行的环境,执行的顺序等。
要使用它的子类 OneTimeWorkRequest 或 PeriodicWorkRequest。WorkManager
管理任务请求和任务队列,发起的 WorkRequest 会进入它的任务队列。WorkStatus
包含有任务的状态和任务的信息,以 LiveData 的形式提供给观察者,更新相关UI
二、如何使用:
依赖,在 build.gradle 添加如下依赖:
//workmanager
api 'androidx.work:work-runtime:2.2.0'
- 第一步:自定义Work类,继承Worker类,这里以上传文件为示例
public class UploadFileWorker extends Worker {
public UploadFileWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
Data inputData = getInputData();
String filePath = inputData.getString("file");
String fileUrl = FileUploadManager.upload(filePath);
if (TextUtils.isEmpty(fileUrl)){
return Result.failure();
}else{
Data outputData = new Data.Builder().putString("fileUrl", fileUrl)
.build();
return Result.success(outputData);
}
}
}
- 第二步:定义 WorkRequest
这里我们使用OneTimeWorkRequest 这个Request,因为我们的request不是轮循的任务,故使用OneTimeWorkRequest,在MainActiivty中创建OneTimeWorkRequest如下所示:
Data inputData = new Data.Builder()
.putString("file", filePath)
.build();
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.setInputData(inputData)
.build();
UUID uploadRequestId= request.getId();
//每一个request对应一个UUID,通过这个ID,可以监听该任务的一些状态及执行结果
- 第三步:将定义好的WorkRequest加入到队列,如下所示,和Okhttp请求差不多
WorkContinuation workContinuation = WorkManager.getInstance(PublishActivity.this)
.beginWith(request);
workContinuation.enqueue();
//通过返回的LiveData,监听任务的执行结果及UI更新
workContinuation.getWorkInfosLiveData().observe(PublishActivity.this, new Observer>() {
@Override
public void onChanged(List workInfos) {
//block runing enuqued failed susscess finish
for (WorkInfo workInfo : workInfos) {
WorkInfo.State state = workInfo.getState();
Data outputData = workInfo.getOutputData();
UUID uuid = workInfo.getId();
if (state == WorkInfo.State.FAILED) {
if (uuid.equals(fileUploadUUID)) {
showToast(getString(R.string.file_upload_original_message));
//TODO
}
} else if (state == WorkInfo.State.SUCCEEDED) {
String fileUrl = outputData.getString("fileUrl");
if (uuid.equals(fileUploadUUID)) {
fileUploadUrl = fileUrl;
}
//TODO
}
}
}
});
三、数据交互
在实际开发业务中,执行后台任务的时候,都会传递参数,这里来具体讲解下WorkRequest是如何进行数据传递
WorkRequest已经为我们设计好了,在创建WorkRequest的时候,通过构建一个Data对象,传入必要的参数,上述案例中需要上传文件,故需传递文件对象的路径
在UploadFileWorker 中,通过 getInputData(); 方法,将得到传递的参数。类似于Bundle的使用
Data inputData = new Data.Builder()
.putString("file", filePath)
.build();
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.setInputData(inputData)
.build();
在UploadFileWorker 中doWork()方法中,执行完异步任务后,需要传递参数的时候,也可以通过cData,将需要的参数返回出去,即Result.success(outputData)。
Data outputData = new Data.Builder().putString("fileUrl", fileUrl)
.build();
Result.success(outputData);
如果需要取消一个在队列中的任务,,每一个Request对应一个ID,所以可以通过 id 实现取消任务
UUID uploadRequestId= request.getId();
WorkManager.getInstance().cancelWorkById(request.id)
到这里,相信对WorkManager的使用有了基本的了解,接下来看WorkManager的高级用法
四、高级特性
4.1 环境约束
WorkManager 允许我们指定任务执行的环境,比如网络已连接、电量充足时等,在满足条件的情况下任务才会执行。
具体设置方法如下:
@SuppressLint("RestrictedApi") Constraints constraints = new Constraints();
//设备存储空间充足的时候 才能执行 ,>15%
constraints.setRequiresStorageNotLow(true);
//必须在执行的网络条件下才能好执行,不计流量 ,wifi
constraints.setRequiredNetworkType(NetworkType.UNMETERED);
//设备的充电量充足的才能执行 >15%
constraints.setRequiresBatteryNotLow(true);
//只有设备在充电的情况下 才能允许执行
constraints.setRequiresCharging(true);
//只有设备在空闲的情况下才能被执行 比如息屏,cpu利用率不高
constraints.setRequiresDeviceIdle(true);
//workmanager利用contentObserver监控传递进来的这个uri对应的内容是否发生变化,当且仅当它发生变化了
//我们的任务才会被触发执行,以下三个api是关联的
constraints.setContentUriTriggers(null);
//设置从content变化到被执行中间的延迟时间,如果在这期间。content发生了变化,延迟时间会被重新计算
// 这个content就是指 我们设置的setContentUriTriggers uri对应的内容
constraints.setTriggerContentUpdateDelay(0);
//设置从content变化到被执行中间的最大延迟时间 这个content就是指 我们设置的
constraints.setContentUriTriggers uri对应的内容
constraints.setTriggerMaxContentDelay(0);
通过创建Constraints ,设置具体的约束条件,在创建Request的时候,调用.setConstraints(constraints)即可。
OneTimeWorkRequest request = new OneTimeWorkRequest
.Builder(UploadFileWorker.class)
.setInputData(inputData)
.setConstraints(constraints)
// .setConstraints(constraints)
// //设置一个拦截器,在任务执行之前 可以做一次拦截,去修改入参的数据然后返回新的数据交由worker使用
// .setInputMerger(null)
// //当一个任务被调度失败后,所要采取的重试策略,可以通过BackoffPolicy来执行具体的策略
// .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, TimeUnit.SECONDS)
// //任务被调度执行的延迟时间
// .setInitialDelay(10, TimeUnit.SECONDS)
// //设置该任务尝试执行的最大次数
// .setInitialRunAttemptCount(2)
// //设置这个任务开始执行的时间
// //System.currentTimeMillis()
// .setPeriodStartTime(0, TimeUnit.SECONDS)
// //指定该任务被调度的时间
// .setScheduleRequestedAt(0, TimeUnit.SECONDS)
// //当一个任务执行状态编程finish时,又没有后续的观察者来消费这个结果,难么workamnager会在
// //内存中保留一段时间的该任务的结果。超过这个时间,这个结果就会被存储到数据库中
// //下次想要查询该任务的结果时,会触发workmanager的数据库查询操作,可以通过uuid来查询任务的状态
// .keepResultsForAtLeast(10, TimeUnit.SECONDS)
.build();
设置完constraints的Request,则加入到队列中的时候 workContinuation.enqueue();不会立即执行,只有当constraints中的条件满足的时候,才会执行该request 。
4.2 强大的生命力
这是 WorkManager 的另一个特点,一旦发起一个任务,任务是可以保证一定会被执行的,就算退出应用,甚至重启手机都阻止不了他。但可能由于添加了环境约束等原因,它执行的时间是不确定的。
当应用正在运行时,它会在当前的进程中启用一个子线程执行。应用没有运行的情况下启用,它则会自己选择一种合适的方式在后台运行。具体是什么方式和 Android 的版本和依赖环境有关:
4.3 任务链:
WorkManager 允许我们按照一定的顺序执行任务,比如我想 A、B、C 三个任务按先后顺序执行:
则可以使用如下代码,即可实现:
OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
OneTimeWorkRequest requestB = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
OneTimeWorkRequest requestC = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
WorkManager.getInstance(PublishActivity.this).beginWith(requestA).then(requestB).then(requestC).enqueue();
这样的话,上一个任务的 outputData 会成为下一个任务的 inputData。
再更更复杂一点,如果我想这样:
OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
OneTimeWorkRequest requestB = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
OneTimeWorkRequest requestC = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
OneTimeWorkRequest requestD = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
OneTimeWorkRequest requestE = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
WorkContinuation chainA = WorkManager.getInstance(PublishActivity.this).beginWith(requestA).then(requestB);
WorkContinuation chainB = WorkManager.getInstance(PublishActivity.this).beginWith(requestC).then(requestD);
List chains =new ArrayList<>();
chains.add(chainA);
chains.add(chainB);
WorkContinuation.combine(chains).then(requestE).enqueue();
4.5 任务唯一性
很多情况下,我们希望在任务队列里,同一个任务只存在一个,避免任务的重复执行,这时候可以用到 beginUniqueWork 这个方法:
WorkManager.getInstance()
.beginUniqueWork("unique", ExistingWorkPolicy.REPLACE, request)
.enqueue()
需要传入一个任务的标签,和重复任务的执行方式,可取值如下:
但这种方式也是只支持 OneTimeWorkRequest。如果是 PeriodicWorkRequest,我想到的办法是每次执行之前,根据标签去取消已有的任务。
4.6 使用场景
很明显,WorkManager 区别于异步任务,它更像是一个 Service。基本上,WorkManager 能做的,Service 也能做,我并没有想到有什么情况是非用 WorkManger 不可的。
但反观 Service,泛滥的 Service 后台任务可能是引起 Android 系统卡顿的主要原因,这几年 Google 也对 Service 也做了一些限制。
到这里,WorkManager的基本使用就介绍完了,文章中的示例代码已上Jetpack
该仓库为演示Jetpack的组件的仓库,分别对Lifecyele、LiveData、ViewModel、Room的介绍和使用
详细介绍文章
Android Jetpack架构组件-Lifecycle使用
Android Jetpack架构组件-LiveData使用
Android Jetpack架构组件-ViewModel的使用及原理
Android Jetpack架构组件-Paging介绍及实践
Android Jetpack架构组件-Room基本使用
Android Jetpack架构组件-Room数据库查询艺术
Android Jetpack架构组件-Room升级