什么是WorkManager
在Android开发中,经常需要在后台执行某些任务
为此,出现了许多种解决方案
比如Service,JobSchedule…
现在,我们也可以用Workmanager来完成这种需求
Workmanager为应用程序中那些不需要及时完成的任务提供了一个统一的解决方案
以便在设备电量和用户体验之间达到一个比较好的平衡
Workmanager最低可以兼容API Level 14
WorkManager兼容方案
Workmanager根据API不同的版本选择了不同的内部实现方案
具体看下图
这里需要注意的,Workmanager不能保证任务可以立即得到执行,也就是不具备即时性。
Workmanager基本使用
简单使用
第一步,添加依赖
implementation "androidx.work:work-runtime:2.4.0-alpha03"
第二步,创建一个简单的Work类,实现doWork方法
class MyWork(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): Result {
Log.d("MyWork", "执行任务")
return Result.success()
}
}
第三步,执行任务,在Activity里面创建一个按钮的点击方法mAddWork,
点击按钮然后将任务放入到WorkManager里面
fun mAddWorks(view: View) {
//设置触发条件
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.NOT_REQUIRED)//没有网络
.build()
//配置任务
//一次性执行的任务
val workRequest1 = OneTimeWorkRequest.Builder(MyWork::class.java)
//设置触发条件
.setConstraints(constraints)
//设置延时执行
// .setInitialDelay(5, TimeUnit.SECONDS)
.build()
val workManager = WorkManager.getInstance(this)
workManager.enqueue(workRequest1)
}
因为WorkManager的任务不具备即时性
这里设置触发条件为断网的情况
是为了方便执行
然后创建一个Request,放入到Workmanager的队列里面
运行代码,点击按钮,就会执行MyWork里面的doWork方法了
注意:
NetworkType是一个枚举类,对应的是不同的网络状态
比如连接wifi,断网等等
Constraints.Builder()除了有网络的触发外,还有其他的触发条件
比如:
setRequiresCharging(boolean requiresCharging)是否在充电时触发
setRequiresBatteryNotLow(boolean requiresBatteryNotLow) 是否在电量不足时执行
更多的参考源码里的注解即可
setInitialDelay可以用来设置延时执行,但一般作用不大
因为WorkManager的任务本身就不具备即时性
什么时候执行是不确定的
指数退避策略
doWork方法有三个返回值
Result.success 任务执行成功
Result.failure 任务执行失败
Result.retry 任务需要重试
当设置重试时,在任务失败后,会根据一定的条件重试
这个条件可以在WorkRequest里面设置指数退避策略,具体方式如下
//配置任务
//一次性执行的任务
val workRequest1 = OneTimeWorkRequest.Builder(MyWork::class.java)
//设置触发条件
.setConstraints(constraints)
//设置延时执行
// .setInitialDelay(5, TimeUnit.SECONDS)
//指数退避策略
.setBackoffCriteria(BackoffPolicy.LINEAR, Duration.ofSeconds(2))
//设置tag标签
.addTag("workRequest1")
.build()
setBackoffCriteria()方法里面传两个参数
第一个传对应的策略
BackoffPolicy.LINEAR:线性的方式延迟;
BackoffPolicy.EXPONENTIAL:指数的方式延迟
第二个传固定的时间
观察任务状态和取消任务
Workmanager可以通过ID和tag的方式观察任务的状态
具体实现如下
//观察任务状态
workManager.getWorkInfoByIdLiveData(workRequest1.id).observe(this, Observer {
Log.d("MainActivity", "任务信息:${it}")
})
通过Tag观察的代码如下:
//观察任务状态
workManager.getWorkInfosByTag("workRequest1").get().forEach {
}
通过Tag返回的是一个任务信息的列表
因为可能查出多个拥有同一个任务的Tag
取消任务也可以通过Tag或者id
workManager.cancelWorkById(workRequest1.id)
workManager.cancelAllWorkByTag("workRequest1")
周期性任务
如果要固定一个时间长度去执行一次任务,可以使用周期性任务
但是注意,Workmanager的周期性任务长度,不能低于15分钟
//周期性任务 时间不能少于15分钟
val workRequest2 =
PeriodicWorkRequest.Builder(MyWork::class.java, Duration.ofMinutes(15)).build()
参数传递
Workmanager如何传递参数给Worker
Worker任务执行完之后,又如何回传参数呢
可以通过androidx.work.Data这个类来实现
比如先定义一个inputData
val inputData = Data.Builder().putString("input_data", "jack").build()
这里面传递了一个字符串过去
WorkRequest里面这样配置
//配置任务
//一次性执行的任务
val workRequest1 = OneTimeWorkRequest.Builder(MyWork::class.java)
//参数传递
.setInputData(inputData)
.build()
Worker类的doWork()方法里可以这样获得传入的参数:
val inputDataStr = inputData.getString("input_data")
Log.d("MyWork", "inputData:${inputDataStr}")
然后也可以自己回传结果给Workmanager
也是用到androidx.work.Data这个类
完整代码如下:
override fun doWork(): Result {
val inputDataStr = inputData.getString("input_data")
Log.d("MyWork", "inputData:${inputDataStr}")
SystemClock.sleep(2000)
Log.d("MyWork", "mywork dowork")
//任务执行完返回数据
val outputData = Data.Builder().putString("output_data", "执行成功").build()
return Result.success(outputData)
}
Workmanager通过观察任务状态,获得doWork()方法里回传的结果
//观察任务状态
workManager.getWorkInfoByIdLiveData(workRequest1.id).observe(this, Observer {
if (it != null && it.state === WorkInfo.State.SUCCEEDED) {
val outputData = it.outputData.getString("output_data")
Log.d("MainActivity", "outputData:${outputData}")
}
Log.d("MainActivity", "toString:${it.toString()}")
})
这样,就完成了Workmanager和Worker任务之间参数的传递了
任务链和任务组合
当有两个任务,需要区分前后顺序时
可以使用Workmanager的任务链
比如WorkA先执行,然后再执行WorkB
就可以这样写
val workA = OneTimeWorkRequest.Builder(Awork::class.java).build()
val workB = OneTimeWorkRequest.Builder(Bwork::class.java).build()
WorkManager.getInstance(this)
.beginWith(workA)
.then(workB)
.enqueue()
再复杂一点,现在有ABCDE五个任务
先执行A、B和C、D
再执行E
如图所示
那么应该如何实现呢?
这就需要使用到任务组合了
具体代码如下
val workA = OneTimeWorkRequest.Builder(Awork::class.java).build()
val workB = OneTimeWorkRequest.Builder(Bwork::class.java).build()
val workC = OneTimeWorkRequest.Builder(Cwork::class.java).build()
val workD = OneTimeWorkRequest.Builder(Dwork::class.java).build()
val workE = OneTimeWorkRequest.Builder(Ework::class.java).build()
//任务组合
val workContinuation1 = WorkManager.getInstance(this)
.beginWith(workA)
.then(workB)
//任务组合
val workContinuation2 = WorkManager.getInstance(this)
.beginWith(workC)
.then(workD)
val workContinuationList = listOf<WorkContinuation>(workContinuation1, workContinuation2)
WorkContinuation.combine(workContinuationList)//合并任务组合
.then(workE)
.enqueue()
关于Workmanager就介绍到这里了