WorkManager笔记: 一、基础入门


前言

  • 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 提供了灵活的重试政策,包括可配置的指数退避政策
(也就是任务自动的重试延时策略)
工作链对于复杂的相关工作,您可以使用流畅自然的接口将各个工作任务串联起来,
这样您便可以控制哪些部分依序运行,哪些部分并行运行。
  1. 看起来功能强大又炫酷. 并且使用也很简单方便; 只满足使用层面的话 学习成本也不高
  2. 在各种不同 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 的时间设置 会方便 WorkManager 在宽松的时间要求内, 能够在最佳的环境条件下运行任务)

下图显示了可在灵活时间段内运行工作的的重复间隔。

注意:

  • 重复间隔(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()

注意: 以上方式, 任务执行时间似乎会逐渐延后.
也可以改用一次性任务, 然后在任务结尾处 启动新的单次任务, 通过计算延迟来修正执行时间;

二、工作状态

无论 一次性工作 还是 定期动作, 它们的初始状态都是 ENQUEUEDENQUEUED 状态下,工作会在满足其 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 个退避政策,即 LINEAREXPONENTIAL

每个工作请求都有默认的 退避政策和退避延迟时间。默认政策是 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: 10秒, 20秒, 40秒, 80秒

不知道有没有退避上限次数.


总结

以上就是本篇的内容, 文篇主要讲述了 创建工作到工作执行、区分一次性及定期工作、工作约束(工作执行条件), 退避重试规则等.
但还知道如何观察工作, 管理工作. 比如取消或停止工作等; 下一篇文章将会讲述这些内容;

上一篇: Service: 三、小窗口(浮窗) 播放视频
下一篇: WorkManager笔记: 二、管理工作

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值