最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面。
Android Architecture组件是Android Jetpack的一部分,它们是一组库,旨在帮助开发者设计健壮、可测试和可维护的应用程序,包含一下组件:
上述时Android Architecture所提供的架构组件,本文将详细介绍下WorkManger的使用
WorkManger是Android Jetpack提供执行后台任务管理的组件,它适用于需要保证系统即使应用程序退出也会运行的任务,WorkManager API可以轻松指定可延迟的异步任务以及何时运行它们,这些API允许您创建任务并将其交给WorkManager立即运行或在适当的时间运行。
WorkManager根据设备API级别和应用程序状态等因素选择适当的方式来运行任务。如果WorkManager在应用程序运行时执行您的任务之一,WorkManager可以在您应用程序进程的新线程中运行您的任务。如果您的应用程序未运行,WorkManager会选择一种合适的方式来安排后台任务 - 具体取决于设备API级别和包含的依赖项,WorkManager可能会使用 JobScheduler,Firebase JobDispatcher或AlarmManager
3.1、使用WorkManager之前需先了解几个类:
3.2、工作流程
def work_version = "1.0.0-alpha10"
implementation "android.arch.work:work-runtime:$work_version" // use -ktx for Kotlin
androidTestImplementation "android.arch.work:work-testing:$work_version"
class TestWorker(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
override fun doWork(): Result {
Log.e("TestWorker", "执行了 doWork() 操作!")
return Result.SUCCESS
}
}
val workRequest = OneTimeWorkRequest.Builder(TestWorker::class.java).build()
WorkManager.getInstance().enqueue(workRequest)
WorkManager.getInstance().getStatusByIdLiveData(workRequest.id) // 返回LiveData
.observe(this, Observer {
Log.e("TestWorker", it?.state?.name)
if (it?.state!!.isFinished) {
Log.e("TestWorker", "Finish")
}
})
从上面执行过程中看出,回调了Work的三个运行状态RUNNING、SUCCESSESD、FINISH
// 在连接网络、插入电源且设备处于空闲时运行
val myConstraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresCharging(true)
.setRequiresDeviceIdle(true)
.build()
除了上面设置的约束外,WorkManger还提供了以下的约束作为Work执行的条件:
配置好Constraints后,创建Work对象
val compressionWork = OneTimeWorkRequest.Builder(TestWorker::class.java)
.setConstraints(myConstraints)
.build()
执行结果:在为连接网络时,Work处于阻塞状态当连接网络后Work立即执行变为SUCCESSED状态
WorkManager.getInstance().cancelWorkById(workRequest.id)
OneTimeWorkRequestBuilder()
.addTag("cleanup")
.build()
/./ 使用:
WorkManager.getInstance().getStatusesByTag("TAG")
WorkManager.getInstance().cancelAllWorkByTag("TAG")
val timesRequest = PeriodicWorkRequest.Builder(TestWorker::class.java,10,TimeUnit.SECONDS)
......
.build()
WorkManager.getInstance()
.beginWith(workA)
.then(workB)
.then(workC)
.enqueue()
执行结果:任务会按照设置的顺序依次执行A、B、C
有一点就是WorkManger在执行过程中,当遇到一个WOrk不成功,则会停止执行,现修改WorkB返回FAILURE状态,再次运行程序结果如下:
override fun doWork(): Result {
Log.e("Worker", "WorkerB 执行了 doWork() 操作!")
return Result.FAILURE
}
代码执行到WorkB就已经结束了,WorkC并未执行。
WorkManager.getInstance()
.beginWith(workA1, workA2, workA3) // 三个对象将并行
.then(workB) // 执行完3个A后,在执行B
.then(workC1, workC2) //...
.enqueue()
// ......继续创建WorkD 和 WorkE 及相应的Request
val configA_B = WorkManager.getInstance().beginWith(workRequest)
.then(workRequestB)
val configC_D = WorkManager.getInstance().beginWith(workRequestC)
.then(workRequestD)
WorkContinuation.combine(configA_B,configC_D)
.then(workRequestE)
.enqueue()
执行结果与上面一致:开始时执行A/C,其余为阻塞状态,A/C执行结束后执行B/D,最后执行WoekerE
如果此时把WorkB中返回FAILURE,执行结果也一致执行到WorkerB就结束了,WorkerE不会执行
WorkManager.getInstance().beginUniqueWork("worker", ExistingWorkPolicy.APPEND, workRequest)
//参数:1、工作序列的名称
2、当有相同名称序列时采取的策略方式
3、需要执行的Worker
* ExistingWorkPolicy.REPLACE:取消现有序列并将其替换为新序列
* ExistingWorkPolicy.KEEP:保留现有序列并忽略您的新请求
* ExistingWorkPolicy.APPEND:将新序列附加到现有序列,在现有序列的最后一个任务完成后运行新序列的第一个任务
以ExistingWorkPolicy.REPLACE为例,让WorkA睡眠3秒,模拟同时运行的状态:
override fun doWork(): Result {
Log.e("Worker", "WorkerA begin Sleep()!")
Thread.sleep(3000)
Log.e("Worker", "WorkerA 执行了 doWork() 操作!")
return Result.SUCCESS
}
Append:两个都会执行
WorkManager.getInstance().beginUniqueWork("worker", ExistingWorkPolicy.APPEND, workRequest)
.enqueue()
WorkManager.getInstance().beginUniqueWork("worker", ExistingWorkPolicy.APPEND, workRequestB)
.enqueue()
运行结果:
REPLACE:只会执行WorkerB
KEEP:只会执行WorkerA
修改WorkerA如下
const val MIN_NUMBER = "minNumber"
const val MAX_NUMBER = "maxNumber"
const val RESULT_CODE = "Result"
class WorkerA(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
private var minNumber = 0
private var maxNumber = 0
override fun doWork(): Result {
minNumber = inputData.getInt(MIN_NUMBER, 0) // 使用InputData获取传入的参数
maxNumber = inputData.getInt(MAX_NUMBER, 0)
val result = maxNumber - minNumber // 计算结果
val outData: Data = Data.Builder().putAll(mapOf(RESULT_CODE to result)).build() // 创建返回的数据Data
outputData = outData // 设置返回的数据Data
return Result.SUCCESS
}
}
创建Worker并传递参数
val map = mapOf(MIN_NUMBER to 5, MAX_NUMBER to 15)
val data = Data.Builder().putAll(map).build() // 创建输入参数Data
val mathWork = OneTimeWorkRequestBuilder()
.setInputData(data) // 传递参数
.build()
观察任务WorkStatus获取返回结果
WorkManager.getInstance().getStatusByIdLiveData(workRequest.id)
.observe(this, Observer {
if (it?.state!!.isFinished) {
Log.e("WorkerA", "${it.outputData.getInt(RESULT_CODE, 0)}") // 获取执行结果
}
})
const val NUMBER = "NUMBER"
const val RESULT_B = "RESULT_B"
class WorkerB(context: Context,workerParameters: WorkerParameters) : Worker(context,workerParameters){
override fun doWork(): Result {
val input = inputData.getInt(RESULT_CODE,5) // 获取第一个Worker返回的结果
Log.e("Worker", "WorkerB 接受数据 input = $input ")
val map = mapOf(RESULT_B to input*input)
outputData = Data.Builder().putAll(map).build() // 设置返回数据
return Result.SUCCESS
}
}
// Activity中监听WorkerB的执行结果
WorkManager.getInstance().getStatusByIdLiveData(workRequestB.id)
.observe(this, Observer {
if (it?.state!!.isFinished) {
Log.e("WorkerB", "${it.outputData.getInt(RESULT_B, 0)}")
}
})
执行结果:对于WorkA依次传入5和15,在WorkA中计算差值15-5 = 10,所以WorkA中输出10,那么链式调用的WorkB中接收的就是10,加平方后输出为100,输出Log与分析一致,
到此Android Jetpack组件就介绍完了,从第一篇开始到现在有一个月了,中间断断续续总算分析完了,通过对组件的学习发现Android Jetpack组件的推出不仅加速了我们开发的速度,而且避免了程序运行中的一些生命周期、内存问题以及一些性能问题,希望可以通过这几篇文章对想学习组件的同学有所帮助,后续将会使用所有的组件编写一个客户端,将所有组件综合使用。