前面已经说了很多 jetpack 中的架构组件,每一种的出现都是为了更好的解决现在存在的问题,或许这些问题也有解决方案,可是千人前面,也没有一个规范而言。同样 WorkManager 也是为了解决一些问题,在做开发的时候总避免不了做些后台任务,好比我们的业务埋点的上传,正常我们起定时器也可以实现,但是一旦用户退出了应用,我们的应用到了后台,一旦遇到系统内存吃紧的情况,应用进程就会被杀掉了,遇到这种情况我们的埋点就没法上传了,那对于我们的数据分析可能就会产生影响,之前的话或许我们会尝试很多进程保活的方案,可是每一种几乎都有它自己的局限性,但是 WorkManager 的出现基本解决了这种问题,它可以在应用退出的时候依然执行一些异步任务。用官方的话来讲就是:WorkManager 旨在用于可延迟运行(即不需要立即运行)并且在应用退出或设备重启时必须能够可靠运行的任务。
关于 WorkManager 的使用需要我们了解三个类:WorkManager、WorkRequest 以及 Worker。
我们要执行的具体任务是需要放在继承了 Worker 的类里面的,所以 Worker 里面定义了做什么,继承了 Worker 后我们需要重写 doWork 方法,好比我们在里面实现上传埋点的业务逻辑,最终会返回一个 Result,因为业务方是需要知道我们要执行的任务的执行情况的,对吧?这个 Result 表示我们要执行的任务的执行结果情况,如果成功了需要我们调用 Result.success(),失败了则需要调用 Result.failure()。但是这个结果怎么用呢?我们怎么获取到这个执行结果呢?或者说这个 Result 会响应到哪里呢?这个我们后面再说。
上面的 Worker 只是定义了我们要在后台做什么,但是怎么做,做几次之类的需要 WorkRequest 来定义。所以一个 Worker 需要经过 WorkRequest 的包装,WorkRequest 有两个子类,如果是只执行一次的任务我们用 OneTimeWorkRequest,如果是需要定时执行的我们用 PeriodicWorkRequest。假设我们的埋点上传后台任务,其实当手机没有网络的时候是没有必要上传的,之前的话可能触发方法了还需要我们自己去判断是否有网络进而决定是否上传了,但是使用了 WorkManager,我们可以直接在任务执行之前就把这些我们需要的配置给配置好,这一切都需要 Constraints 来做,Constraints 是一个对 Worker 的执行添加约束条件的类,具体可配置的有:网络状态、设备的充电状态、关于电量状态、关于内存状态等等,好比我们刚刚的关于网络的需求就可以通过 setRequiredNetworkType(NetworkType.CONNECTED) 来实现,传入的 NetworkType.CONNECTED 就代表只有网络连接的情况下才会执行任务。
我们说了 Constraints 是对任务执行添加的约束条件,也就意味着只有我们设置的条件都满足了任务才会执行,同样如果我们在任务执行期间,突然某个条件不满足了,任务会停止运行。当所有的任务都满足的时候,才会重新触发任务的执行。我们可能想在 work 开始执行的时候传递一些参数过去,可以通过调用 WorkRequest 的 setInputData 传递一个 Data 类型的参数进去,Data 的数据结构类似于 map,也是以 key-value 的方式来存储以及获取数据。设置了参数,我们在 Worker 里面怎么获取呢?直接在对应的 Worker 里面的 doWork 里面 getInputData 获取我们刚刚传入的 Data,通过对应的 key 获取就好了。
我们上面说了 Worker 可以把执行任务的结果同步给业务方,但是能否携带一些数据过去呢?好比成功了,传递一些成功的数据回去,即使失败了也传递一些失败的原因回去。所以我们是可以在 Result.success() 以及 Result.failure() 里面传递参数的,里面的参数类型依然是 Data 的,而在获取方那里获取的也是一个 Data 类型的数据,这里需要注意的是 Data 里面存储的数据不能超过 10KB。那这里传递的数据又如何被获取呢?其实这个问题是如何获取一个已经在执行的 WorkRequest,我们可以通过 id 可以通过 tag,还可以通过 uniquename,id 的话每个 WorkRequest 都自带一个,但是 tag、uniquename 需要我们自己去设置,这俩又有啥区别呢?多个 WorkRequest 可以有同一个 tag,但是一个 WorkRequest 对应一个 uniquename。
uniquename 顾名思义就是为了保持 WorkRequest 任务执行的唯一性而存在的。好比我们给一个 workrequest 设置了 uniquename,那么当下次再有一个相同的 uniquename 的 WorkRequest 启动的时候是会冲突的,我们必须指定这种冲突策略,是替换已经那个已经存在的 uniquename 的 WorkRequest(意为删除之前的,开始新的),还是维持之前的(意为让之前的运行,当前的要运行的不再执行),还是把要新启动的 workrequest 当做已经存在的一个 workrequest 的子 workrequest。
所以当我们给 workrequest 指定了 tag 以及 uniquename 后我们就可以通过这个来获取对应的 workerRequest 了。那又该如何获取呢?可以通过 getInfoByIdLiveData、getWorkInfosForUniqueWorkLiveData 以及 getWorkInfosByTagLiveData,我们可以看到获取的都是 LiveData,到了 LiveData 我们就熟悉了,它是可以被 observer 的,我们上面的 worker 的 触发 Result 之后都会相应到 observer 里面,在这里我们可以获取到任务的执行结果,并且可以获取到对应的任务执行的状态,例如是成功了,还是失败了,这些我们可以通过 observer 里面监控到的 workInfo 的 state 获取到,其中的 workinfo 里面存储的是对应的 workrequest 的信息。
如果说 Worker 定义了做什么任务,WorkRequest 定义了如何做,那 WorkManager 就是对 WorkRequest 的管理,我们有时候可能会有这样的需求就是想要一下子执行多个任务,如果不是并行执行的话可能还想指定执行顺序,如果这个由我们自己来做也可以,但是需要加很多的监听回调会很麻烦,幸亏这一切 workmanager 帮我们想好了。我们可以通过 workmanager 的 beginUniqueWork 或者 beginWith() 来得到一个 WorkContinuation,它可以组织 WorkRequest 如何执行,可以通过 then 方法指定执行顺序,而 beginWith 也可以传入一个 workrequest 的 list 来并发执行一组任务。