绝大部分应用程序都有在后台执行任务的需求,根据需求的不同,Android为后台任务提供了多种解决方案,如JobScheduler、Loader、Service等。WorkManager为应用程序中那些不需要及时完成的任务提供了一个统一的解决方案,以便在设备电量和用户体验之间达到一个比较好的平衡。
WorkManager的3个重要特点:
- 针对的是不需要及时完成的任务
- 保证任务一定会被执行
- 兼容范围广。最低能兼容API Level 14
WorkManager的兼容方案:
WorkManager能依据设备的情况,选择不同的执行方案,在API Level 23以上的设备中,通过JobScheduler完成任务;在API Level 23以下的设备中,通过AlarmManager+BroadcastReceiver组合来完成任务,但无论采用哪种方案,任务最终都是交由Executor来执行的。
使用方法
- 添加依赖
在app的build.gradle中添加依赖
implementation 'androidx.work:work-runtime:2.4.0'
- 使用Worker类定义任务
新建一个名为MyWorker的类,继承自Worker类,并且覆盖都doWork()方法,所有需要在任务中执行的代码都在该方法中进行编写。
class MyWorker(context: Context, params: WorkerParameters): Worker(context, params) {
override fun doWork(): Result {
return Result.success()
}
}
doWork()方法有3种类型的返回值
- 若执行成功,则返回Result.success()
- 若执行失败,则返回Result.failure()
- 若需要重新执行,则返回Result.retry()
- 使用WorkRequest配置任务
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresCharging(true)
.setRequiresBatteryNotLow(true)
.build()
- 将任务触发条件设置到WorkRequest
WorkRequest是一个抽象类,它有两种实现方式——OneTimeWorkRequest和PeriodicWorkRequest,分别对应的是一次性任务和周期性任务。
val oneTimeWorkRequest = OneTimeWorkRequest.Builder(MyWorker::class.java)
.setConstraints(constraints)
.build()
val oneTimeWorkRequest = OneTimeWorkRequest.Builder(MyWorker::class.java)
.setConstraints(constraints)
//延迟10分钟后执行
.setInitialDelay(10, TimeUnit.MINUTES)
.build()
- 设置指数退避策略
假如Worker线程的执行出现了异常,希望过一段时间后重试该任务,那么可以在Worker的doWork()方法种返回Result.retry(),系统会有默认的指数退避策略来进行重试,也可以通过setBackoffCriteria()方法来自定义指数退避策略。
val oneTimeWorkRequest = OneTimeWorkRequest.Builder(MyWorker::class.java)
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.MINUTES)
.build()
val oneTimeWorkRequest = OneTimeWorkRequest.Builder(MyWorker::class.java)
.setConstraints(constraints)
.addTag("MyWorkerTag")
.build()
- 将任务提交给系统
WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)
- 观察任务的状态
任务提交给系统后,可以通过WorkInfo获知任务的状态。WorkInfo包含任务的id、tag、Worker对象传递过来的outputData,以及任务的当前状态。有3种方法可以得到WorkInfo对象:
- WorkManager.getInstance(this).getWorkInfoById(oneTimeWorkRequest.id)
- WorkManager.getInstance(this).getWorkInfosByTag(“MyWorkerTag”)
- WorkManager.getInstance(this).getWorkInfosForUniqueWork(“uniqueWorkName”)
如果希望实时获取任务状态,三个方法还有对应的LiveData方法:
- WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest.id)
- WorkManager.getInstance(this).getWorkInfosByTagLiveData(“MyWorkerTag”)
- WorkManager.getInstance(this).getWorkInfosForUniqueWorkLiveData(“uniqueWorkName”)
通过LiveData可以在任务状态发生变化收到通知:
WorkManager.getInstance(this)
.getWorkInfoByIdLiveData(oneTimeWorkRequest.id)
.observe(this, {
workInfo -> Log.d("onChanged", "workInfo: $workInfo")
})
- 取消任务
WorkManager.getInstance(this).cancelAllWork()
- WorkManager与Worker之间的参数传递
WorkManager通过setInputData()方法想Worker传递数据。数据的传递通过Data对象来完成。需要注意的是,Data只能用于传递一些小的基本类型的数据,且数据最大不能超过10KB。
Worker通过getInputData()方法接收数据,并在任务完成后,向WorkManager返回数据。
val inputData = Data.Builder().putString("input_data", "My Worker Data").build()
val oneTimeWorkRequest = OneTimeWorkRequest.Builder(MyWorker::class.java)
.setConstraints(constraints)
.setInputData(inputData)
.build()
override fun doWork(): Result {
//接收外面传递进来的数据
val inputData = inputData.getString("input_data")
//任务执行完成后返回数据
val outputData = Data.Builder().putString("output_data", "Success").build()
return Result.success(outputData)
}
WorkManager通过LiveData得到从Worker返回的数据。
WorkManager.getInstance(this)
.getWorkInfoByIdLiveData(oneTimeWorkRequest.id)
.observe(this, { workInfo ->
run {
if (workInfo?.state == WorkInfo.State.SUCCEEDED) {
val outputData = workInfo.outputData.getString("output_data")
Log.d("onChanged", "outputData: $outputData")
}
}
})
- 周期性任务PeriodicWorkRequest
周期性任务会按照设定的时间定期执行,间隔时间不能少于15分钟。
val periodicWorkRequest = PeriodicWorkRequest.Builder(MyWorker::class.java, 20, TimeUnit.MINUTES)
.setConstraints(constraints)
.addTag("Periodic MyWorker")
.build()
- 任务链
如果有一系列的任务需要按顺序执行,那么可以利用WorkManager.beginWith().then().then()…enqueue()的方式构建任务链。
WorkManager.getInstance(this).beginWith(oneTimeWorkRequest1).then(oneTimeWorkRequest2).enqueue()
假设有更复杂的任务链,还可以考虑用WorkContinuation.combine()方法将任务链组合起来。
val workContinuation1 =
WorkManager.getInstance(this).beginWith(oneTimeWorkRequest1).then(oneTimeWorkRequest2)
val workContinuation2 =
WorkManager.getInstance(this).beginWith(oneTimeWorkRequest3).then(oneTimeWorkRequest4)
val taskList = mutableListOf(workContinuation1, workContinuation2)
WorkContinuation.combine(taskList).then(oneTimeWorkRequest5).enqueue()
- 结论
WorkManager会根据系统的版本,决定采用JobSheduler或AlarmManager+BroadcastReceiver来完成任务。但这些API很可能会收到非原生系统的影响。
同时周期性任务的实际执行时间,与所设定的时间差别较大,并且没有明显的规律。在任务执行完成后,WorkInfo并不会收到Success的通知。Android认为Success和Failure都属于“终止类”的通知,意味着,若发出此类通知,则表明任务“彻底终止”,而周期性任务是不会彻底终止的,它会一直执行下去,因此使用LiveData观察周期性任务时,不会 收到Success这一类的通知。