文章目录
- 前言
- WorkManager 介绍
- WorkManager 优势
- WorkManager 导包
- 一、简单使用:
- 1. 简单工作步骤(一次性 和 定期工作):
- 2.延时工作:
- 3.灵活的运行间隔
- 二、工作状态
- 一次性工作的状态:
- 定期工作的状态
- 三、工作约束
- 1.约束类型
- 2.约束写法
- 四、重试和退避政策
- 总结
前言
Service 保活的话题, 流行了很久. 更是在许多面试官口中问出. 开发者们做保活 以及 谷歌官方反保活 可谓是一场拉锯战. 可以看出,谷歌并不希望 应用在用户不知道的情况下, 偷偷的跑业务. 这样也不利于电池续航
-
如果真的有需要保活的业务, 可以尝试
用带通知栏的前台Service, 视频浮窗用 画中画, 后台异步任务用作业 或 WorkManager 等;
实在不行再考虑保活;
Android 12(API 31) 引入了前台服务启动限制, 如果您的应用受到该限制影响, 请改用 WorkManager;
WorkManager 介绍
官方介绍:
WorkManager
可轻松调度那些即使在退出应用
或重启设备
后仍应运行的可靠异步任务。它可以替换
先前所有的后台调度 API
(包括 FirebaseJobDispatcher、GcmNetworkManager 和 JobScheduler); 建议统一使用 WorkManager
。该 API 最低支持 API 级别 14,在开发时即考虑到了对电池续航的影响。
适用性:
-
适用于:
-
不适用:
内部适配方案:
WorkManager 会根据设备版本, 自行选择不同的处理方案, 如图:
WorkManager 优势
有的小伙伴会问, 不就是后台异步任务吗 我用 Job, Service 不是一样能完成吗?
没错, 那就让我们看看用 WorkManager
值不值得;
功能 | 描述 |
工作约束 | 约束可确保将工作延迟到满足最佳条件时运行; 例如: 网络环境(是否WIFI), 电量是否不足, 是否充电, 设备是否空闲, 存储是否充足 |
强大的调度 | 调度灵活, 可运行 内部用SQLite存储任务状态, 由 WorkManager 负责确保该工作持续进行,并在设备重新启动后重新调度。 此外,WorkManager 遵循低电耗模式等省电功能和最佳做法,因此您在这方面无需担心。 |
灵活的重试政策 | 有时工作会失败。WorkManager 提供了灵活的重试政策,包括可配置的指数退避政策 (也就是任务自动的重试延时策略) |
工作链 | 对于复杂的相关工作,您可以使用流畅自然的接口将各个工作任务串联起来, 这样您便可以控制哪些部分依序运行,哪些部分并行运行。 |
看起来功能强大又炫酷. 并且使用也很简单方便; 只满足使用层面的话 学习成本也不高
-
在各种不同 Android API版本上, 都能使用统一的处理方案; 避免繁杂的适配问题
WorkManager 导包
// Java
implementation "androidx.work:work-runtime:2.7.0"
// Kotlin
implementation "androidx.work:work-runtime-ktx:2.7.0"
一、简单使用:
我们先写出简单 一次性 和 定期工作的例子; 再进行说明
1. 简单工作步骤(一次性 和 定期工作):
首先自定义工作类. 继承 Worker
, 在重写的 doWork()
class MyWorker(appContext: Context, workerParams: WorkerParameters):
Worker(appContext, workerParams) {
override fun doWork(): Result {
// 需要后台执行的 业务代码
...
// 指示工作已成功完成, 也可以根据情况定义失败或重试; (WorkManager有重试策略)
return Result.success()
}
}
再定义 WorkRequest(工作请求)
; 它将指定工作的执行要求; (如: 工作约束, 调度信息, 重试, 加急 等)
// 定义一次性工作
val myWorkRequest: WorkRequest = OneTimeWorkRequestBuilder<MyWorker>().build()
// 定义 重复工作; 这里指定 工作的间隔时间 最小为 1个小时
val myWorkRequest: WorkRequest = PeriodicWorkRequestBuilder<MyWorker>(1, TimeUnit.HOURS).build()
最后, 将工作请求 交给 WorkManager
WorkManager.getInstance(this).enqueue(myWorkRequest)
从 doWork() 返回的 Result 会通知 WorkManager 服务工作是否成功,以及工作失败时是否应重试工作。
- Result.success(): 工作成功完成。
- Result.failure(): 工作失败。
- Result.retry(): 工作失败,应根据其重试政策在其他时间尝试。(重试策略后面再讲)
在WorkManager中, 无论何种工作, 都只需要三步:
- 创建工作(后台工作执行的业务代码)
- 创建工作请求(WorkRequest), 将会定义工作的 执行要求, 执行规则等;
- 将工作请求 提交给 WorkManager, 并等待执行;
官方对 WorkRequest 的介绍:
- WorkRequest 本身是抽象基类。它有两个派生实现,可用于创建
OneTimeWorkRequest
(一次性工作) 和PeriodicWorkRequest
(定期工作) 请求。 - WorkRequest 对象包含
WorkManager
调度和运行工作所需的所有信息。其中包括运行工作必须满足的约束、调度信息(例如延迟或重复间隔)、重试配置,并且可能包含输入数据(如果工作需要)。 - 执行工作器的确切时间取决于
WorkRequest 中使用的约束和系统优化方式
。WorkManager 经过设计,能够在满足这些约束的情况下提供最佳行为。 - 重复任务也是如此, 定义的间隔时间只是 两次重复执行之间的最短时间.
总之:
关乎工作应当如何运行的事儿, 都由 WorkRequest 配置;
-
无论是定时也好, 延时也好, 工作约束也好. 工作是否执行, 具体何时执行 最终取决于WorkManager的优化设计
2.延时工作:
不管一次性工作, 还是重复工作, 都是使用 setInitialDelay(10, TimeUnit.SECONDS)
// 任务提交, 到任务执行, 至少延迟10分钟
val myWorkRequest: OneTimeWorkRequest = OneTimeWorkRequestBuilder<MyWorker>()
.setInitialDelay(10, TimeUnit.SECONDS)
.build()
注意: 定期工作设置延时, 只有首次运行时会延迟。
3.灵活的运行间隔
如果重复执行的工作, 要求的规则是, 每小时的最后10分钟, 每天的0点-1点 执行等; 那么适用于该种方式:
下面的例子是: 每小时的最后15分钟执行任务;
val myWorkRequest = PeriodicWorkRequestBuilder<MyWorker>(
1, TimeUnit.HOURS, // repeatInterval (the period cycle)
15, TimeUnit.MINUTES) // flexInterval
.build()
WorkManager.getInstance(this).enqueue(myWorkRequest)
前两个参数(1小时), 代表任务重复执行的周期; 后两个参数, 代表周期末尾, 任务执行的具体时段
(博主猜测, 因约束, 配额等因素, 导致任务的具体执行时间并不确切, 因此 flexInterval
下图显示了可在灵活时间段内运行工作的的重复间隔。
注意:
重复间隔(repeatInterval) 必须大于或等于 PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS (15分钟)
-
灵活间隔(flexInterval) 必须大于或等于 PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS。(5分钟)
假如我们想, 在每天0-1点执行任务:
只需要 间隔时间为 24小时, 灵活时间为 1小时; 然后设置延迟启动, 延迟时间为, 当前时间到 凌晨1点的时差
val myWorkRequest = PeriodicWorkRequestBuilder<MyWorker>(
24, TimeUnit.HOURS, // repeatInterval (the period cycle)
1, TimeUnit.HOURS) // flexInterval
.setInitialDelay(10, TimeUnit.SECONDS)
.build()
注意: 以上方式, 任务执行时间似乎会逐渐延后.
也可以改用一次性任务, 然后在任务结尾处 启动新的单次任务, 通过计算延迟来修正执行时间;
二、工作状态
无论 一次性工作 还是 定期动作, 它们的初始状态都是 ENQUEUED
在 ENQUEUED
状态下,工作会在满足其 Constraints
一次性工作的状态:
工作转入 RUNNING
状态,然后可能会根据工作的结果转为 SUCCEEDED、FAILED
状态;或者,如果结果是 retry,它可能会回到 ENQUEUED
状态。在此过程中,随时都可以取消工作,取消后工作将进入 CANCELLED
SUCCEEDED、FAILED 和 CANCELLED
均表示此工作的终止状态。如果您的工作处于上述任何状态,WorkInfo.State.isFinished() 都将返回 true。
定期工作的状态
定期工作只有一个终止状态 CANCELLED
。这是因为定期工作永远不会结束。每次运行后,无论结果如何,系统都会重新对其进行调度。
三、工作约束
加入我们的工作任务, 需要在WIFI环境下运行, 需要在充电时运行 等, 这些执行条件 即为 工作约束
约束可确保将工作延迟到满足最佳条件时运行。
1.约束类型
类型 | 意义 |
NetworkType | 约束运行工作所需的网络类型。例如 Wi-Fi (UNMETERED)。 |
BatteryNotLow | 如果设置为 true,那么当设备处于“电量不足模式”时,工作不会运行。 |
RequiresCharging | 如果设置为 true,那么工作只能在设备充电时运行。 |
DeviceIdle | 如果设置为 true,则要求用户的设备必须处于空闲状态,才能运行工作。如果您要运行批量操作, 否则可能会降低用户设备上正在积极运行的其他应用的性能,建议您使用此约束。 |
StorageNotLow | 如果设置为 true,那么当用户设备上的存储空间不足时,工作不会运行。 |
网络状态:
状态条件 | 意义 |
NOT_REQUIRED | 不需要网络, 没有要求 |
CONNECTED | 需要网络, 任意网络 |
UNMETERED | 无限流量网络(WIFI) |
NOT_ROAMING | 非漫游网络 |
METERED | 流量计费网络 |
TEMPORARILY_UNMETERED | 需要 API30; 不知道 |
2.约束写法
约束 统一使用 Constraints类 进行配置;
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED) // WIFI 环境下运行
.setRequiresCharging(true) // true 充电时运行
.setRequiresBatteryNotLow(true) // true 不在低电量模式运行
.setRequiresDeviceIdle(true) // true 设备空闲状态才运行
.setRequiresStorageNotLow(true) // true 存储空间充足时才运行
.build()
val myWorkRequest = OneTimeWorkRequestBuilder<MyWorker>()
.setConstraints(constraints) // 设置约束
.build()
WorkManager.getInstance(this).enqueue(myWorkRequest)
注意:
如果在定期工作加入约束, 除非满足约束条件,否则即使过了定义的重复间隔,PeriodicWorkRequest 也不会运行。这可能会导致工作在某次运行时出现延迟,甚至会因在相应间隔内未满足条件而被跳过。
四、重试和退避政策
如果您需要让 WorkManager
重试工作,可以从工作器返回 Result.retry()
。然后,系统将根据 退避延迟时间
和 退避政策
退避延迟时间:任务失败(Result.retry())时, 到首次尝试重试工作前的最短等待时间。此值不能超过 10 秒
退避政策:任务失败, 后续重试过程中,退避延迟时间的增长方式。WorkManager 支持 2 个退避政策,即
LINEAR
和 EXPONENTIAL
每个工作请求都有默认的 退避政策和退避延迟时间。默认政策是 EXPONENTIAL,延迟时间为 10 秒
写法如下:
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setBackoffCriteria(
BackoffPolicy.LINEAR,
WorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build()
我们假设, 退避延迟时间为 10秒, 两种退避政策下的 重试间隔最小时间分别为:BackoffPolicy.LINEAR:
10秒, 20秒, 30秒, 40秒BackoffPolicy.EXPONENTIAL:
不知道有没有退避上限次数.
总结
以上就是本篇的内容, 文篇主要讲述了 创建工作到工作执行、区分一次性及定期工作、工作约束(工作执行条件), 退避重试规则
等.
但还知道如何观察工作, 管理工作. 比如取消或停止工作等; 下一篇文章将会讲述这些内容;