系统提供的一个新的任务形式,旨在替代Service,因为Service比较重,且前台感知能力太强(需要发送通知),因此Google推出了WorkManager,并对任务做了一些限制,以更好的适配系统任务调度,起到降低开发成本,降低设备功耗的作用
创建work
class DemoWork(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
override fun doWork(): Result {
// 拿到输入的参数
val string = inputData.getString("inputData") ?: ""
Log.d("WorkManagerHelper", string)
return Result.success() // 成功
//return Result.failure() // 失败
//return Result.retry() // 重试
}
// 不是一定要实现的方法
override fun onStopped() {
super.onStopped()
}
}
注意:
1、onStopped的回调,并不代表任务执行完毕,而是在用户手动触发了终止,或者由于系统原因,此任务无法再执行时,也会触发这个回调
2、通过Result.success(),failure(),Result.retry()的方式回调任务执行结果
work
val inputData = Data.Builder().putString("inputData", "这是传入的数据").build()
val request = OneTimeWorkRequest.Builder(DemoWork::class.java)
WorkManager.getInstance(context).enqueue(request)
同上
/**
* 周期性任务,最低时间间隔为15分钟,如果设置的低于15分钟,系统会默认给你设置为 15分钟
* 代码逻辑看这里:WorkSpec.setPeriodic()
*/
fun submitPeriodicWorker(context: Context) {
// 周期性的任务,每次间隔30分钟
val request = PeriodicWorkRequest.Builder(DemoWork::class.java, 30, TimeUnit.MINUTES)
.build()
WorkManager.getInstance(context).enqueue(request)
}
注意:
DemoWork:重写doWork来执行任务
按照上述方式创建的任务,执行间隔为每15分钟一次
重复任务的间隔时间最低为15分钟,超过这个时间间隔,系统将会默认设置为15分钟
/**
* 设置约束
* 如果当前不满足约束条件,则这个任务会处于挂起状态,等待满足约束条件时才会触发
*/
fun buildConstraintsWorker(fragment: Fragment) {
val config = Constraints.Builder()
.setRequiresDeviceIdle(true) // 是否设备空闲时触发
.setRequiresCharging(true) // 是否要求充电时触发
.setRequiredNetworkType(NetworkType.NOT_REQUIRED) // 对网络状态的现实策略
.setRequiresBatteryNotLow(true) // 要求设备不需要处于低电量(通常是20%,设备厂商设置的)
.setRequiresStorageNotLow(true) // 要求设备空间充足
.addContentUriTrigger(Uri.parse(""), false) // 监听URI发生变化,比如在图库里面插入了一张图片
.build()
val request = PeriodicWorkRequestBuilder(24, TimeUnit.SECONDS)
.setConstraints(config)
.build()
WorkManager.getInstance(fragment.requireContext()).enqueue(request)
}
object ChainWorker {
class A(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
override fun doWork(): Result {
val data = Data.Builder().putString("data", "A任务的结果").build()
return Result.success(data)
}
}
class B(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
override fun doWork(): Result {
val string = inputData.getString("data")
// string就是A传递过来的数据
return Result.success()
}
}
class C(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
override fun doWork(): Result {
return Result.success()
}
}
class D(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
override fun doWork(): Result {
return Result.success()
}
}
class E(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
override fun doWork(): Result {
return Result.success()
}
}
/**
* 创建链式任务
* 注意:第一个任务失败时,第二个任务也不会继续走了
*/
fun chainWorker(context: Context) {
val a = OneTimeWorkRequest.Builder(A::class.java).build()
val b = OneTimeWorkRequest.Builder(B::class.java).build()
// 先执行a,再执行b
WorkManager.getInstance(context).beginWith(a)
.then(b)
.enqueue()
}
/**
* 同时执行
*/
fun submitZipTask(context: Context){
val a = OneTimeWorkRequestBuilder().build()
val b = OneTimeWorkRequestBuilder().build()
WorkManager.getInstance(context).beginWith(listOf(a,b)).enqueue()
}
/**
* 混合链式任务
* AB串行,CD串行,两个串之间并行,最后再执行e
*/
fun buildCombineWorker(context: Context) {
val a = OneTimeWorkRequest.Builder(A::class.java).build()
val b = OneTimeWorkRequest.Builder(B::class.java).build()
val c = OneTimeWorkRequest.Builder(C::class.java).build()
val d = OneTimeWorkRequest.Builder(D::class.java).build()
val e = OneTimeWorkRequest.Builder(E::class.java).build()
val chain1 = WorkManager.getInstance(context).beginWith(a).then(b)
val chain2 = WorkManager.getInstance(context).beginWith(c).then(d)
WorkContinuation.combine(listOf(chain1,chain2)).then(e).enqueue()
}
}
实时监听执行状态回调
通过liveData的形式来获取当前Work的执行结果,liveData需要监听宿主的生命周期,因此需要传入lifeCycler对象
WorkManager.getInstance(fragment.requireContext()).getWorkInfoByIdLiveData(request.id).observe(fragment) { workInfo ->
val state = workInfo.state
}
获取任务的当前状态
通过request.getId()可以获取到work的任务id,然后通过这个id即可获取当前任务的状态,注意这个id时系统生成的,不需要用户手动创建
val workInfo = WorkManager.getInstance(fragment.requireContext()).getWorkInfoById(request.id).get()
/**
* 创建只执行一次的任务
*
* 注意:退避策略需要配合Result.retry()
* 如果直接返回了Result.failure(),则退避策略不会生效的
*/
fun createOneTimeWork(context: Context) {
val tag = "多个任务可以设置一个TAG,然后可以通过这一个TAG批量获取任务"
val inputData = Data.Builder().putString("inputData", "这是传入的数据").build()
val request = OneTimeWorkRequest.Builder(OneTimeWork::class.java)
.setInitialDelay(10, TimeUnit.MICROSECONDS) // 10毫秒后再执行
.keepResultsForAtLeast(2, TimeUnit.DAYS) // 任务至少会保留一天,WorkManager默认会保留一天的执行结果,可以通过这个变量修改执行结果默认的保存时间
.setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.MINUTES) // 设置退避策略,比如失败一次后,间隔10分钟后再试,再失败则间隔20分钟,30分钟,40分钟以此类推
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 2, TimeUnit.MINUTES) //指数级增长,失败一次后,间隔10分钟,20分钟,40分钟,80分钟,160分钟。。。
.setInputData(inputData)
.addTag(tag)
.build()
val id = request.id
WorkManager.getInstance(context).enqueue(request)
// 根据任务TAG取消任务
WorkManager.getInstance(context).cancelAllWorkByTag(tag)
// 根据任务id取消任务
WorkManager.getInstance(context).cancelWorkById(id)
}
注意:
退避策略需要结合 Result.retry()执行,如果返回的是success()或者是failure(),则退避策略无效
keepResultsForAtLeast可以设置任务执行结果的保留时间,2.5版本以下是默认保留7天,2.5以上默认是保留一天,如果用户设置了这个值,则至少会保留这个值的时间,同时为了防止内存占用过大,不建议设置太大的值
/**
* 创建唯一任务
* ExistingWorkPolicy.REPLACE:如果队列之前有任务,直接干掉,传入新的
* ExistingWorkPolicy.KEEP:如果之前有任务还在,本次插入无效
* ExistingWorkPolicy.APPEND:拼到上一个任务的后面
*/
fun buildSingleWorker(fragment: Fragment) {
val singleTaskName = "唯一的任务id"
val request = OneTimeWorkRequest.Builder(OneTimeWorker.OneTimeWork::class.java).build()
// 创建唯一任务
WorkManager.getInstance(fragment.requireContext()).beginUniqueWork(singleTaskName, ExistingWorkPolicy.REPLACE, request).enqueue()
WorkManager.getInstance(fragment.requireContext()).enqueueUniqueWork(singleTaskName, ExistingWorkPolicy.REPLACE, request)
// 监听这个唯一任务,根据任务名称来监听
WorkManager.getInstance(fragment.requireContext()).getWorkInfosForUniqueWorkLiveData(singleTaskName).observe(fragment){ workInfoList->
workInfoList.forEach {
// 这里可以根据任务的id,来获取指定的任务
}
}
}
注意:
创建任务时,要求传入任务策略:
ExistingWorkPolicy.REPLACE :表示入队任务队列里面存在这个任务,则会执行替换操作
ExistingWorkPolicy.KEEP:如果之前有任务还在,本次插入无效
ExistingWorkPolicy.APPEND:拼到上一个任务的后面
监听任务执行过程,因为存在拼接的情况,监听的任务回调是一个list,此时可以根据创建时的id来判断具体是哪一个任务
应用被杀死后无法执行的,当应用重新启动后,之前提交的work会被激活,这个时候需要判断任务是否已经添加到队列中,避免重复添加
应用启动后,由于task会被立即激活,而任务中需要依赖的某些参数可能还没有初始化,这个时候需要判空
Work的激活时机在Application的onCreate之后,因此可以考虑在Application中初始化需要的参数