WorkManager
WorkManager很适合用于处理一些要求定时执行的任务,它可以根据操作系统的版本自动选择底层是使用AlarmManager实现还是JobScheduler实现。另外,它还支持周期性任务、链式任务处理等功能
WorkManager和Service没有直接的联系。Service是Android系统的四大组件之一,它在没有被销毁的情况下是一直保持在后台运行的。而WorkManager只是一个处理定时任务的工具,它可以保证即使在应用退出甚至手机重启的情况下,之前注册的任务仍然将会得到执行,因此WorkManager很适合用于执行一些定期和服务器进行交互的任务,比如周期性地同步数据
另外,使用WorkManager注册的周期性任务不能保证一定会准时执行,这并不是bug,而是系统为了减少电量消耗,可能会将触发时间临近的几个任务放在一起执行,这样可以大幅度地减少CPU被唤醒的次数,从而有效延长电池的使用时间
WorkManager的基本用法
添加依赖
implementation "androidx.work:work-runtime:2.2.0"
定义后台任务
定义一个后台任务,并实现具体的任务逻辑
创建SimpleWorker类,继承Worker类,并调用它唯一的构造函数。然后重写父类中的doWork()方法,在这个方法中编写具体的后台任务逻辑
class SimpleWorker(context: Context, workerParameters: WorkerParameters) :
Worker(context, workerParameters) {
companion object {
private const val TAG = "SimpleWorker"
}
override fun doWork(): Result {
Log.d(TAG, "doWork: do work")
return Result.success()
}
}
doWork()方法不会运行在主线程当中,因此你可以放心地在这里执行耗时逻辑,成功就返回Result.success(),失败就返回Result.failure()。除此之外,还有一个Result.retry()方法,它其实也代表着失败,只是可以结合WorkRequest.Builder的setBackoffCriteria()方法来重新执行任务
配置后台任务
配置该后台任务的运行条件和约束信息,并构建后台任务请求
// 配置后台任务的运行条件和约束信息,OneTimeWorkRequest用于构建单次运行的后台任务请求
val request = OneTimeWorkRequest.Builder(SimpleWorker::class.java).build()
或者构建周期性运行的后台任务请求
// PeriodicWorkRequest构建周期性运行的后台任务请求,传入的运行周期间隔不能短于15分钟
val request = PeriodicWorkRequest.Builder(SimpleWorker::class.java, 15, TimeUnit.MINUTES).build()
提交后台任务
将该后台任务请求传入WorkManager的enqueue()方法中,系统会在合适的时间运行
// 将构建出的后台任务请求传入WorkManager的enqueue()方法中,系统就会在合适的时间去运行
WorkManager.getInstance(this).enqueue(request)
使用WorkManager处理复杂的任务
指定的延迟时间后运行
val request = OneTimeWorkRequest.Builder(SimpleWorker::class.java)
// setInitialDelay 让后台任务在指定的延迟时间后运行
.setInitialDelay(5, TimeUnit.MINUTES)
.build()
添加标签
val request = OneTimeWorkRequest.Builder(SimpleWorker::class.java)
// setInitialDelay 让后台任务在指定的延迟时间后运行
.setInitialDelay(5, TimeUnit.MINUTES)
//addTag添加请求标签
.addTag("simple")
.build()
添加了标签的一个功能就是我们可以通过标签来取消同一标签的所有后台任务请求
WorkManager.getInstance(this).cancelAllWorkByTag("simple")
没有标签,也可以通过id来取消单个后台任务请求
WorkManager.getInstance(this).cancelWorkById(request.id)
一次性取消所有后台任务请求
WorkManager.getInstance(this).cancelAllWork()
失败的话重新执行任务
doWork()方法中返回Result.retry(),结合setBackoffCriteria()方法来重新执行任务的
val request = OneTimeWorkRequest.Builder(SimpleWorker::class.java)
// setInitialDelay 让后台任务在指定的延迟时间后运行
.setInitialDelay(5, TimeUnit.MINUTES)
//addTag添加请求标签
.addTag("simple")
// 失败的话重新执行
.setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.SECONDS)
.build()
setBackoffCriteria()方法接收3个参数:第二个和第三个参数用于指定在多久之后重新执行任务,时间最短不能少于10秒钟;第一个参数则用于指定如果任务再次执行失败,下次重试的时间应该以什么样的形式延迟,可选值有两种,分别是LINEAR和EXPONENTIAL,前者代表下次重试时间以线性的方式延迟,后者代表下次重试时间以指数的方式延迟
对后台任务的运行结果进行监听
val workInfoByIdLiveData =
WorkManager.getInstance(this).getWorkInfoByIdLiveData(request.id)
workInfoByIdLiveData.observe(this) { workInfo ->
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
Log.d(TAG, "onCreate: SUCCEEDED")
} else if (workInfo.state == WorkInfo.State.FAILED) {
Log.d(TAG, "onCreate: FAILED")
}
}
调用getWorkInfoByIdLiveData()方法,并传入后台任务请求的id,会返回一个LiveData对象。然后我们就可以调用LiveData对象的observe()方法来观察数据变化了,以此监听后台任务的运行结果;也可以调用getWorkInfosByTagLiveData()方法,监听同一标签名下所有后台任务请求的运行结果
链式任务
假设这里定义了3个独立的后台任务:同步数据、压缩数据和上传数据。现在我们想要实现先同步、再压缩、最后上传的功能,就可以借助链式任务来实现
val syncRequest = ...
val compressRequest = ...
val uploadRequest = ...
WorkManager.getInstance(this)
.beginWith(syncRequest)
.then(compressRequest)
.then(uploadRequest)
.enqueue()
beginWith()方法用于开启一个链式任务,使用then()方法来连接后面的后台任务。另外WorkManager还要求,必须在前一个后台任务运行成功之后,下一个后台任务才会运行。也就是说,如果某个后台任务运行失败,或者被取消了,那么接下来的后台任务就都得不到运行了
WorkManager的所有功能,在国产手机上都有可能得不到正确的运行。这是因为绝大多数的国产手机厂商在进行Android系统定制的时候会增加一个一键关闭的功能,允许用户一键杀死所有非白名单的应用程序。而被杀死的应用程序既无法接收广播,也无法运行WorkManager的后台任务