Android Jetpack架构组件-WorkManager使用篇

一、定义:

作为 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 的版本和依赖环境有关:

image.png

4.3 任务链:

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


image.png

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

         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。

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

image.png

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

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


image.png

但这种方式也是只支持 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升级

项目目录结构为如下

image.png

你可能感兴趣的:(Android Jetpack架构组件-WorkManager使用篇)