前言
在上一篇中,主要讲解了Jetpack对应Navigation组件,在这一篇中,将会对应WorkManager进行详解!
1、WorkManager概念
1.1 WorkManager的作用
在后台执行任务的需求时非常常见的,Android也提供了多种解决方案,如JobSchedule、Loader、Service等,如果这些API没有被恰当使用,则可能会消耗大量电量。Android在解决应用程序耗电问题上做了各种尝试,从Doze到App Standby,通过各种方式限制和管理应用程序,以保证应用陈谷不会在后台消耗过多的设备电量。WorkManager为应用程序中那些不需要及时完成的任务提供了一个统一的解决方案,以便在设备电量和用户体验之间达到一个比较好的平衡。
1.2 WorkManger的特点
-
针对的是不需要及时完成的任务
- 比如:发送应用程序日志、同步应用程序数据、备份用户数据等,这些任务一般都不需要立即完成,如果我们自己来管理这些任务,逻辑可能会非常复杂,若API使用不恰当,可能会消耗大量电量。
-
保证任务一定会执行
- WorkManger 能保证任务一定会执行,即使应用程序当前不在运行中,甚至在设备重启过后,任务仍然会在适当的时刻被执行。这是因为WorkManger有自己的数据库,关于任务的所有信息和数据都能保存在对应数据库中。因此只要任务交给了WorkManager,哪怕应用程序彻底退出,或者设备被重新启动,WorkManger依然能够保证完成任务。 (谷歌原生系统才100%生效,国内真机并不是很管用!慎用!)
-
兼容范围广
- WorkManger最低能兼容API Level 14,并且不需要你的设备安装Google Play Services。因此,不用过于担心兼容性问题,因为API Level 14 已经能够兼容几乎100%的设备了。
1.3 WorkManger兼容方案
如图所示
WorkManager 能根据设备的情况,选择不同的执行方案。在API Level 23以上的设备中,通过JobScheduler完成任务,在API Level23以下的设备中,通过AlarmManager和Broadcast Receivers组合来完成任务。但无论采用哪种方案,任务最终都是由Executor来执行的。
另外,WorkManager不是一种新的工作线程,它的出现不是为了替代其他类型的工作线程。工作线程通常立即运行,并在任务执行完成后给用户反馈。而WorkManager不是及时的,它不能保证任务能立即得到执行。
概念说了一大堆了,那么该如何使用呢?
2、WorkManager使用
2.1 添加依赖
implementation 'androidx.work:work-runtime:2.4.0' // WorkManager
//下面都是协程相关的
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0'
2.2 使用Work类定义任务
class MyWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): Result {
//执行任务前获取对应的参数
val inputData: String? = inputData.getString("input_data")
Log.d("hqk", "inputData:$inputData")
//SystemClock.sleep(2000);
//过程中执行对应的任务
//SystemClock.sleep(2000);
Log.d("hqk", "MyWork doWork")
//任务执行完之后,返回数据
val outputData = Data.Builder()
.putString("output_data", "执行成功")
.build()
//带参数返回
return Result.success(outputData)
}
}
2.3 使用WorkRequest配置任务
2.3.1 设置任务触发条件
//设置触发条件
val constraints =
Constraints.Builder()
// .setRequiredNetworkType(NetworkType.CONNECTED)//网络连接时执行
// .setRequiresBatteryNotLow(true) //不在电量不足执行
// .setRequiresCharging(true) //在充电时执行
// .setRequiresStorageNotLow(true) //不在存储容量不足时执行
// .setRequiresDeviceIdle(true) //在待机状态下执行 调用需要API级别最低为23
// NetworkType.NOT_REQUIRED:对网络没有要求
// NetworkType.CONNECTED:网络连接的时候执行
// NetworkType.UNMETERED:不计费的网络比如WIFI下执行
// NetworkType.NOT_ROAMING:非漫游网络状态
// NetworkType.METERED:计费网络比如3G,4G下执行。
//注意:不代表恢复网络了,就立马执行
.setRequiredNetworkType(NetworkType.NOT_REQUIRED)
.build()
2.3.2 设置对应任务参数
//设置参数
val inputData = Data.Builder()
.putString("input_data", "jack")
.build()
2.3.3 配置任务
//配置任务
//一次性执行的任务
val workRequest1 = OneTimeWorkRequest.Builder(MyWork::class.java)
//设置触发条件
.setConstraints(constraints)
//设置延迟执行
.setInitialDelay(5, TimeUnit.SECONDS)
//指数退避策略
.setBackoffCriteria(BackoffPolicy.LINEAR, Duration.ofSeconds(2))
//设置tag标签
.addTag("workRequest1")
//参数传递
.setInputData(inputData)
.build()
2.3.4 将配置好的任务提交给WorkManager
//任务提交给WorkManager
val workManager = WorkManager.getInstance(this)
workManager.enqueue(workRequest1) //对应配置好的任务
2.3.5 通过WorkManager观察任务状态
//观察任务状态
workManager.getWorkInfoByIdLiveData(workRequest1.id)//对应的任务id
.observe(this, {
Log.d("hqk", it.toString())
if (it != null && it.state == WorkInfo.State.SUCCEEDED) {
val outputData = it.outputData.getString("output_data")
Log.d("hqk", "outputData:$outputData")
}
})
2.3.6 取消任务
//取消任务
launch {
delay(2000)
//同理,通过对应任务id取消对应任务,当然也可以一次性全取消对应workManager的所有任务
workManager.cancelWorkById(workRequest1.id)
Log.d("hqk","cancel Work")
}
上面将是单次任务单次运行的配置。那如果说同一个任务想让它周期性执行该怎样呢?
2.3.7 配置周期性任务
// //配置任务
// //一次性执行的任务
// val workRequest1 = OneTimeWorkRequest.Builder(MyWork::class.java)
// //设置触发条件
// .setConstraints(constraints)
// //设置延迟执行
// .setInitialDelay(5, TimeUnit.SECONDS)
//指数退避策略
// .setBackoffCriteria(BackoffPolicy.LINEAR, Duration.ofSeconds(2))
// //设置tag标签
// .addTag("workRequest1") //参数传递
// .setInputData(inputData)
// .build()
//周期性任务
//不能少于15分钟
val workRequest2 = PeriodicWorkRequest.Builder(MyWork::class.java, Duration.ofMinutes(15))
// .setConstraints()
.setInputData(inputData)
.build()
//任务提交给WorkManager
val workManager = WorkManager.getInstance(this)
workManager.enqueue(workRequest2) //这里就得使用任务2
//下面任务监听,对应也需要改成任务2
注意:周期性任务,对应周期不能少于15分钟!
上面演示的都是,单次任务对应的单次运行或周期运行!那如果想多个任务组合运行该是怎样呢?
如图所示
想要实现的效果为:A和B、C和D先后执行,两者执行完后,再来执行E!
2.3.8 任务链组合配置
如图所示
这里定义了对应的任务,每个任务里面将会打印对应任务的内容,而对应的逻辑在SecondActivity里面。
现在来看看这SecondActivity实现了哪些逻辑!
class SecondActivity : AppCompatActivity(), CoroutineScope by MainScope() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun mAddWork(view: View) {
//初始化对应的Work
val workA = OneTimeWorkRequest.Builder(AWorker::class.java)
.build()
val workB = OneTimeWorkRequest.Builder(BWorker::class.java)
.build()
val workC = OneTimeWorkRequest.Builder(CWorker::class.java)
.build()
val workD = OneTimeWorkRequest.Builder(DWorker::class.java)
.build()
val workE = OneTimeWorkRequest.Builder(EWorker::class.java)
.build()
//任务组合(先A后B)
val workContinuation1 = WorkManager.getInstance(this)
.beginWith(workA)
.then(workB)
//任务组合(先C后D)
val workContinuation2 = WorkManager.getInstance(this)
.beginWith(workC)
.then(workD)
val taskList: MutableList<WorkContinuation> = ArrayList()
//将对应的组合添加至对应的集合
taskList.add(workContinuation1)
taskList.add(workContinuation2)
//先通过combine将组合的任务列表添加至E任务之前
WorkContinuation.combine(taskList)
.then(workE)
.enqueue()
}
}
一切尽在注释中!
来看看运行效果
完美运行!
结束语
好了,本篇到这就结束了!相信你对WorkMangerr有一定的了解!在下一篇中,将会讲解Jetpack对应的Paging!