🍓 简介:
WorkManager 是一个 API,它可以很合理的安排可延迟的异步任务,即使应用程序退出或者设备重新启动,这些异步任务也有望运行.WorkManager API是所有以前Android 后台调度api 的合适的且推荐的替代品,包括FirebaseJobDispatcher.GcmNetworkManager,和 Job Scheduler.WorkManager 在现在,一致的 api 中合并了其前辈的功能,该 API 可以兼容到 API 14,同事考虑了电池的寿命.
虽然 Service也可以实现,但是消耗电量大,而且并不允许长时间后台执行.PASS 掉.
特点
- 🦋 不需要及时完成的任务.比如 发送日志,同步用户数据等
- 🦋 保证任务一定会被执行.即使 app不在运行或者重启设备.
- 🦋 兼容范围广.最低到API level 14.并且不需要安装 google paly service.
🍓 原理:
🍓 使用方法:
先看看几个关键的类.
🥝 Worker : 任务的执行者,是一个抽象类,需要集成它来实现要执行的任务.
🥝 WorkRequest : 指定哪个 Worker 执行任务,可以向WorkRequest中添加细节,指定执行环境,执行顺序,ID 等.WorkRequest是个抽象类,在代码中是,使用其子类,OneTimeWorkRequest 或者PeriodicWorkRequest.
🥝 WorkManager :对WorkRequest进行排队和管理.
🥝 WorkStatus :包含任务的状态和信息,以 LIiveData 的形式提供给观察者.
1,build.gradle 添加依赖
// Java
implementation "androidx.work:work-runtime:2.5.0"
// kotlin workmanager
implementation "androidx.work:work-runtime-ktx:2.5.0"
2,使用 Work 类定义任务
class MyWoker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): Result {
//耗时任务在 doWork()中执行
return Result.success()
}
}
doWork() 有 3 种返回类型
- 执行成功 Result.success()
- 执行失败 Result.failure()
- 重新执行 Result.retry()
3, 使用 WorkRequest 分配任务
WorkRequest是一个抽象类,它有两种实现方式,OneTimeWorkRequest 和 PeriodicWorkRequest,分别对应一次性任务和周期性任务.
🐮 定义WorkRequest
OneTimeWorkRequest :
var build = OneTimeWorkRequest.Builder(MyWoker::class.java).build()
PeriodicWorkRequest :
var build = PeriodicWorkRequest.Builder(MyWoker::class.java, 15, TimeUnit.MINUTES).build()
🦁以下的代码示例中,我会以OneTimeWorkRequest写 Demo.
🦁PeriodicWorkRequest使用和OneTimeWorkRequest没有太大区别,需要注意的是,间隔时间不能少于 15 分钟.
🐮 设置任务的触发条件:
比如,我们在设备处于充电,网络连接的情况下触发任务,通过.setConstraints(constraints)
方法设置触发条件
var constraints = Constraints.Builder()
.setRequiresCharging(true)
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
var build = OneTimeWorkRequest.Builder(MyWoker::class.java)
.setConstraints(constraints)
.build()
看下图还有很多其他的条件,具体使用什么条件,根据需求定吧.
🐮 设置任务延迟:
通过
.setInitialDelay(10,TimeUnit.SECONDS)
设置延时条件
var build = OneTimeWorkRequest.Builder(MyWoker::class.java)
.setConstraints(constraints)
.setInitialDelay(10,TimeUnit.SECONDS)
.build()
🐮 设置指数退避策略:
假如Work线程执行异常,在 doWork()方法中返回 Result.retry(),系统会有默认的的指数退避策略来重试任务.也可以通过.setBackoffCriteria(BackoffPolicy.LINEAR,OneTimeWorkRequest.MIN_BACKOFF_MILLIS,TimeUnit.MINUTES)
来定义指数退避策略.BackoffPolicy有两个值LINEAR(每次重试的时间线性增加,比如第一次10分钟,第二次就是20分钟)、EXPONENTIAL(每次重试时间指数增加)。
var build = OneTimeWorkRequest.Builder(MyWoker::class.java)
.setConstraints(constraints)
.setInitialDelay(10,TimeUnit.SECONDS)
.setBackoffCriteria(BackoffPolicy.LINEAR,OneTimeWorkRequest.MIN_BACKOFF_MILLIS,TimeUnit.MINUTES)
.build()
🐮 为任务设置 tag 标签:
设置 tag 标签后,可以根据 tag 来跟踪任务的状态:WorkManager.getInstance(this).getWorkInfosByTag()
,也可以取消任务: WorkManager.getInstance(this).cancelAllWorkByTag()
.
var build = OneTimeWorkRequest.Builder(MyWoker::class.java)
.setConstraints(constraints)
.setInitialDelay(10,TimeUnit.SECONDS)
.setBackoffCriteria(BackoffPolicy.LINEAR,OneTimeWorkRequest.MIN_BACKOFF_MILLIS,TimeUnit.MINUTES)
.addTag("myTag")
.build()
4, 将任务交给系统,加入任务队列
WorkManager.getInstance(this).enqueue(build)
🐮 观察任务状态:
观察任务的状态的方法如下:
我们可以通过 LivaData 在任务状态发生变化的的时候接收通知:
WorkManager.getInstance(this)
.getWorkInfosByTagLiveData("myTag")
.observe(this,
{
Log.e("TestActivity", "workInfo: $it")
})
🐮 取消任务:
取消任务的方法如下:
与观察任务类似,可以根据 id 或者 tag 来取消某个任务或者取消所有任务.
WorkManager.getInstance(this).cancelAllWorkByTag("myTag")
5, WorkManager 和 Worker 之间的参数传递
WorkManager通过setInputData()方法给 Worker 传递参数.数据的传递通过 Data 对象来完成.
⚠️ Data 只能用于传递一些小的数据类型,切数据不能超过 10KB.
var inputData = Data.Builder().putString("nameS", "张三").build()
var build = OneTimeWorkRequest.Builder(MyWoker::class.java)
.setInputData(inputData)
//......
.addTag("myTag")
.build()
Woker 通过 getInputData()方法接受数据,并在任务完成后,向 WorkManager 返回数据.
class MyWoker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): Result {
inputData.getString("nameS")?.let { Log.e("mmm", "MyWoker收到的信息: $it") }
var outData = Data.Builder().putString("nameM", "我已经回复张三了").build()
return Result.success(outData)
}
}
WorkManager通过 LiveData 得到从 Worker 返回数据
WorkManager.getInstance(this)
.getWorkInfosByTagLiveData("myTag")
.observe(this, {
for (workInfo in it) {
Log.d("mmm", workInfo.toString())
if (WorkInfo.State.SUCCEEDED.isFinished) {
val outputData = workInfo.outputData
val nameM = outputData.getString("nameM")
nameM?.let { it1 ->
Log.e("mmm", "TestActivity收到的信息: $it1")
WorkManager.getInstance(this@TestActivity).cancelAllWorkByTag("myTag")
}
}
}
})
执行结果截图:
6, 任务链
如果有一系列的任务需要按照顺序执行,那么可以利用WorkManager.beginWith().then().then()....enqueue()
的方式构建任务.beginWith()
可与传递单个任务,也可以传递任务集合.
单个任务:
WorkManager.getInstance(this)
.beginWith(build)
.then(build)
.then(build)
.enqueue()
多个任务:(集合)
WorkContinuation.combine()方法将任务链组合起来用
var buildOne = WorkManager.getInstance(this)
.beginWith(buildOne)
.then(buildTwo)
var buildTwo = WorkManager.getInstance(this)
.beginWith(buildThree)
.then(buildFour)
var list = mutableListOf<WorkContinuation>()
list.add(buildOne)
list.add(buildTwo)
WorkContinuation
.combine(list)
.then(buildFive)
.enqueue()
ps:任务链的东西,我没试过