31

Jetpack架构组件篇—WorkManager使用篇

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzAxMTg2MjA2OA%3D%3D&%3Bmid=2649851863&%3Bidx=4&%3Bsn=e0940410dcb5f4839e808a095dc003b5
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

yEFnief.gif

一、定义:

作为 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<List<WorkInfo>>() {
@Override
public void onChanged(List<WorkInfo> 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 的版本和依赖环境有关:

nAvEjeZ.png!web

4.3 任务链:

WorkManager 允许我们按照一定的顺序执行任务,比如我想 A、B、C 三个任务按先后顺序执行:

jQz6nej.png!web

则可以使用如下代码,即可实现:

         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。

再更更复杂一点,如果我想这样:

eEfIBry.png!web

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<WorkContinuation> 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()

需要传入一个任务的标签,和重复任务的执行方式,可取值如下:

bUNjIvv.png!web

但这种方式也是只支持 OneTimeWorkRequest。如果是 PeriodicWorkRequest,我想到的办法是每次执行之前,根据标签去取消已有的任务。

4.6 使用场景 很明显,WorkManager 区别于异步任务,它更像是一个 Service。基本上,WorkManager 能做的,Service 也能做,我并没有想到有什么情况是非用 WorkManger 不可的。

但反观 Service,泛滥的 Service 后台任务可能是引起 Android 系统卡顿的主要原因,这几年 Google 也对 Service 也做了一些限制。

到这里,WorkManager的基本使用就介绍完了,文章中的示例代码已上Github

https://github.com/OnexZgj/Jetpack_Component

该仓库为演示Jetpack的组件的仓库,分别对Lifecyele、LiveData、ViewModel、Room的介绍和使用

作者:OneXzgj

链接:https://www.jianshu.com/p/e123eaba1e25

---END---

推荐阅读: 【译】使用Kotlin从零开始写一个现代Android 项目-Part1 Jetpack 重磅更新!都有哪些新功能? 首款国产操作系统面世,比Windows更美观? 最流行的 RESTful API 要怎么设计? Jetpack Compose 重磅更新! 神一样的存在,Dagger Hilt !! 四连问:API 接口应该如何设计?如何保证安全?如何签名?如何防重? 【译】使用Kotlin从零开始写一个现代Android 项目-Part1 我用废旧的Android手机做了个机器人 一张“壁纸”引发的三星手机系统崩溃,原因及解决方案!Google最新回应来了!
----------  END  ----------

推荐一个技术号

Github实验室 由国内一线大厂专家、985博士、硕士组成的团体运营。主要分享和研究业界实用、有趣的开源项目,学习资源,开发工具,学术交流。关注就无套路送你一份2020年最新Java面试题及答案(283页)。

fayaQrN.jpg!web

长按二维码识别关注


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK