- 基本概念
WorkManager很适合用于处理一些要求定时执行的任务,它可以根据操作系统的版本自动选择底层是使用AlarmManager实现还是JobScheduler实现,从而降低我们的使用成本。另外,它还支持周期性任务、链式任务处理等功能,是一个非常强大的工具。WorkManager只是一个处理定时任务的工具,它可以保证即使在应用退出甚至手机重启的情况下,之前注册的任务仍然将会得到执行,因此,WorkManager很适合用于执行一些定期和服务器进行交互的任务,比如周期性地同步数据等等。
- WorkManager的基本用法
- 依赖
implementation 'androidx.work:work-runtime:2.2.0'
- 主要分为三步
- 定义一个后台任务,并实现具体的任务逻辑
- 配置该后台任务的运行条件和约束信息,并构建后台任务请求
- 将该后台任务请求传入
WorkManager
的enqueue()
方法中,系统会在合适的时间运行
- 第一步:定义一个后台任务,这里创建一个SimpleWorker类
class SimpleWorker(context: Context, params: WorkerParameters): Worker(context, params) {
override fun doWork(): Result {
Log.e("SimpleWorker", "do work in simpleWorker")
return Result.success()
}
}
后台任务的写法非常固定,首先每一个后台任务都必须继承自Worker类,并调用它唯一的构造函数,然后重写父类的
doWork()
方法,在这个方法里编写具体的后台任务逻辑。doWork()
方法不会运行在主线程中,另外它要求返回一个Result对象,用于表示任务的执行结果,成功返回Result.success(),失败返回Result.failure(),还有一个Result.retry(),它也代表着失败,只是可以结合WorkRequest.Builder的setBackoffCriteria()
方法来重新执行任务
- 第二步:配置该后台任务的运行条件和约束信息,这里只进行最基本配置
val request = OneTimeWorkRequest.Builder(SimpleWorker::class.java).build()
OneTimeWorkRequest.Builder
是WorkRequest.Builder
的子类,用于构建单次运行的后台任务请求,WorkRequest.Builder
还有另外一个子类,PeriodicWorkRequest.Builder
,可用于构建周期性运行的后台任务请求,但是为了降低设备性能消耗,PeriodicWorkRequest.Builder
构造函数中传入的运行周期间隔不能短于15分钟,如下:
val request = PeriodicWorkRequest.Builder(SimpleWorker::class.java, 15, TimeUnit.MINUTES).build()
- 第三步:将构建出的后台任务请求传入WorkManager的
enqueue()
方法中,系统就会在合适的时间去运行了
WorkManager.getInstance(context).enqueue(request)
doWorkBtn.setOnClickListener {
val request = OneTimeWorkRequest.Builder(SimpleWorker::class.java).build()
WorkManager.getInstance(this).enqueue(request)
}
- 使用WorkManager处理复杂的任务
- 让后台任务延迟指定时间后运行,只需借助setInitialDelay()方法
val request = OneTimeWorkRequest.Builder(SimpleWorker::class.java)
.setInitialDelay(5, TimeUnit.MINUTES)
.build()
这就表示我们希望让
SimpleWorker
这个后台任务在5分钟后运行,你可以自由地选择时间的单位,毫秒、秒、分钟、小时、天都可以。
- 给后台任务请求添加标签,以便取消任务
val request = OneTimeWorkRequest.Builder(SimpleWorker::class.java)
.setInitialDelay(5, TimeUnit.MINUTES)
.addTag("simple")
.build()
- 通过标签取消后台任务请求
workManager.getInstance(this).cancelAllWorkByTag("simple")
- 当然,即使没有标签,我们也可以通过id来取消后台任务请求
workManager.getInstance(this).cancelWorkById(request.id)
- 但是,使用id只能取消单个后台任务请求,而使用标签的话,则可以将同一标签名的所有后台任务请求全部取消
- 除此之外,我们也可以使用如下代码来一次性取消所有后台任务请求
workManager.getInstance(this).cancelAllWork()
- 如果后台任务的doWork()方法中返回了Result.retry(),那么是可以结合setBackoffCriteria()方法来重新执行任务的
val request = OneTimeWorkRequest.Builder(SimpleWorker::class.java)
.setInitialDelay(5, TimeUnit.MINUTES)
.addTag("simple")
.setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.SECONDS)
.build()
setBackoffCriteria()
方法接收三个参数:第二个和第三个参数用于指定在多久之后重新执行任务,时间最短不能少于10秒钟;第一个参数则用于指定如果任务再次执行失败,下次重试的时间应该以什么样的形式延迟;假如任务一直执行失败,不断地重新执行似乎并没有什么意义,只会徒增设备的性能消耗,而随着失败次数的增多,下次重试的时间也应该进行适当的延迟,这才是更加合理的机制,第一个参数的可选值有两种,分别是LINEAR和EXPONENTIAL,前者代表下次重试时间以线性的方式延迟,后者代表下次重试时间以指数的方式延迟。
- 我们可以使用如下代码对后台任务的运行结果进行监听
WorkManager.getInstance(this)
.getWorkInfoByIdLiveData(request.id)
.observe(this){ workInfo ->
if(workInfo.state == WorkInfo.State.SUCCESS){
}else if(WorkInfo.state == WorkInfo.State.FAILED){
}
}
这里调用
getWorkInfoByIdLiveData()
方法,并传入后台任务请求的id,会返回一个LiveData
对象,然后调用它的observe()
方法来观察数据变化了,以此监听后台任务的运行结果。也可以调用getWorkInfoByTagLiveData()
方法,监听同一标签名下所有后台任务请求的运行结果,用法是差不多的。
- 再来看一下WorkManager中比较有特色的一个功能--链式任务
假设这里定义了三个独立的后台任务:同步数据、压缩数据、上传数据,现在我们想要实现先同步、再压缩、最后上传的功能,就可以借助链式任务来实现
val sync = ...
val compress = ...
val upload = ...
WorkManager.getInstance(this)
.beginWith(sync)
.then(compress)
.then(upload)
.enqueue()
beginWith()
方法用于开启一个链式任务,至于后面要接上什么样的后台任务,只需要使用then()
方法来连接即可。另外,WorkManager还要求,必须在前一个后台任务运行成功之后,下一个后台任务才会运行,也就是说,如果某个后台任务运行失败了,或者取消了,那么接下来的后台任务就都得不到运行了。