今天的主角是我们还没关注到的几个WorkManager
的功能和细节。
Constraints
WorkManager里面有一个Constraints
—— 姑且在这儿称它为限制,它的用处就是:保证一定的限制条件满足后,才执行任务请求。
在任务请求的builder里,有一个方法就用于设置限制条件,WorkRequest.Builder.setConstraints(Constraints)
,即WorkRequest
运行的前提条件。
1 |
|
从Constraints
类的定义中看出,包括网络情况、充电状态、存储容量等,都可以作为限制条件。
实验
接下来,用网络作为一个限制条件,来实操下。
给DelayWorker添加toast,提醒任务执行:
1 |
|
网络限制:
1 |
|
(1) 断网启动应用,执行结果:
1 |
|
虽然添加了任务(enqueued),但任务并未执行。
(2) 联网后:
1 |
|
此时,日志打印显示任务执行,toast也弹出了。
Operation
到目前为止,我们enqueue的所有任务,都像是“嫁出去的女儿,泼出去的水”,并没有关注到底有没有“嫁”成功 —— 其实enqueue和cancel,都有返回值的啊!
1 |
|
这个返回值Operation
,就是用来监听WorkManager的执行状态的,包括三种:SUCCESS
(执行成功)、IN_PROGRESS
(执行中)和FAILURE
(执行失败)
1 |
|
实验
下面给单次任务添加一个Operation监听:
1 |
|
结果:
1 |
|
添加任务后,Operation就马在经历了 IN_PROGRESS -> SUCCESS
,也就是任务添加成功。接下来就是任务本身的执行及其执行状态的变化。
再来看看取消周期任务
1 |
|
结果:
1 |
|
同样地,取消动作 IN_PROGRESS -> SUCCESS
Result
相信,Result
已经不是一个陌生的东西了,DelayWorker任务结束就调用了它。顾名思义,它就是用于标志任务的执行结果。
1 |
|
其中,Success
、Failure
和Retry
都是隐藏的,需要构造时,调用Result的静态方法就行。
在之前讨论链式任务的时候,已经有用到Failure
了,以此返回的任务,状态为失败FAILED,并丢掉后续的任务。
那么,Retry
怎么用呢?
实验
定义一个可以返回Retry结果类型的Worker:
1 |
|
启动上述任务:
1 |
|
来看看日志打印结果:
1 |
|
看起来,这个任务是没完没了的执行下去了…… 明明是一个onetime任务,结果像搞成了periodic!
下面来详细分析下日志:
1 |
|
由上面的分析日志可以得出结论:
- 预想的第二次任务就是成功Result,没有实现,因为即使重试执行任务,输入参数都将保留。毕竟,既然是retry,那环境肯定需要一样。
- 失败后的retry冷静期等待时间序列变化:
30s -> 60s -> 120s -> 240s -> 480s ...
,第二次再retry后,后续都是按2倍时间延长
retry的冷静期间隔时间是怎么来的?来看看源码吧。
Retry重试执行的源码分析
1 |
|
从上面注释可以知道,retry的等待时间,受WorkRequest.Builder.setBackoffCriteria
影响。几个常量值得关注:DEFAULT_BACKOFF_DELAY_MILLIS
, MAX_BACKOFF_MILLIS
, MIN_BACKOFF_MILLIS
1 |
|
在实际执行任务时,相关参数存在一个WorkSpec
对象里:
1 |
|
指数函数:
1 |
|
默认重试间隔是30秒,那第一次重试间隔:
\[30 * 2^{(1 - 1)} = 30\]第二次重试间隔:
\[30 * 2^{(2 - 1)} = 60\]以此类推。所以,这就是前面日志打印的时间间隔来源。
实验2
有了相关的重试逻辑知识后,修改前面的retry任务为线性重试,初始间隔时间为15秒:
1 |
|
任务的retry与否,简单起见,通过时间来判断:
1 |
|
来看看结果:
1 |
|
带数据结果
前面有提到,Result有两个方法可以返回带数据的Result:
1 |
|
当然,能获取成功执行的任务的数据,是针对链式任务的OneTimeWorkRequest
来说的。
成功数据
生成一个携带数据的DataWorker
1 |
|
构造连续执行的三个DataWorker任务请求:
1 |
|
结果:
1 |
|
后续任务都成功的获取到前一个任务通过Result.success(@NonNull Data outputData)
传入的名称。
失败数据
在任务链那篇讲过,一旦有任务失败,链式任务后续的任务都将fail。所以,带数据的faile,不是在继任任务场景下使用的。那么,带数据的失败结果,如何获取呢?
还记得前面讲任务的状态时关注到的类WorkInfo
吗?
1 |
|
其中,mOutputData
字段是不是就是我们要找的结果数据呢?
改装一下FailedWorker,让它可以抛出失败数据:
1 |
|
构造任务链:
1 |
|
来看看结果:
1 |
|
果然,失败数据存入了WorkInfo
里面。同样地,成功数据也可以通过这种方法获取。
小结
这次讨论了三个点:Constraints
, Operation
, 和 Result
,它们都是在之前的讨论中忽略掉的、但又很有用的功能。
至此,WorkManager比较重要的知识点及其使用,都讲完了。在写这些内容的时候,我有种体会:写文章更能帮助加深对于知识点的理解。所以,加油吧!
再给一下文章中涉及的代码