关于WorkManager需要知道的一切
背景
WorkManager 是google为Android推出Jetpack开发套件的成员之一,是一个持续化工作的推荐解决方案。 我们在开发过程中通常会遇到一些需要持续化的操作需求,例如,上传文件到服务端,Token过期刷新,定期更新服务端下发的配置等。
主要的用途是:
立即开始执行一个任务
有的任务必须立即开始执行,但这里可能会有个疑问,为什么立即开始执行的任务不直接写代码开始执行,要利用WorkManager呢?这主要归功于WorkManager的一些特性,例如根据约束安排任务,链式化任务等,让你代码写得更简洁,更好的扩展性。
需要长时间定期运行的任务
任务需要长时间定期运行的的情况,例如我们我需要每一个小时上传一次用户日志.
延迟任务
有的任务需要延后一段时间执行.
再继续讲解它如何使用之前先来说说它的几个特性.
特性
约束
提供一些约束条件去执行任务,当约束条件满足后才开始执行,例如,当连接上Wifi, 当设备有足够的电量等条件
可靠性
安排的任务保证能够顺利执行,因为WorkManager内部以SqLite存储任务的执行的情况,为执行的和执行失败的都会重新尝试.
加急任务
可能你会给WorkManager安排很多Task, 某些Task也许优先级比较高,需要立即执行,WorkManager提供加急的特性,可以尽早执行这类Task.
链式任务
某些Task可能需要顺序执行,也可能需要并行执行,WorkManager同样提供这类API满足需求
val continuation = WorkManager.getInstance(context)
.beginUniqueWork(
Constants.IMAGE_MANIPULATION_WORK_NAME,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequest.from(CleanupWorker::class.java)
).then(OneTimeWorkRequest.from(WaterColorFilterWorker::class.java))
.then(OneTimeWorkRequest.from(GrayScaleFilterWorker::class.java))
.then(OneTimeWorkRequest.from(BlurEffectFilterWorker::class.java))
.then(
if (save) {
workRequest<SaveImageToGalleryWorker>(tag = Constants.TAG_OUTPUT)
} else /* upload */ {
workRequest<UploadWorker>(tag = Constants.TAG_OUTPUT)
}
)
线程的互操作性
无缝继承了Coroutines和RxJava的异步特性,在WorkManager也能使用这类异步的API
实战
说了这么多,下面我们来看看如何使用WorkManager
第一步,添加依赖
dependencies {
def work_version = "2.8.1"
// (Java only)
implementation "androidx.work:work-runtime:$work_version"
// Kotlin + coroutines
implementation "androidx.work:work-runtime-ktx:$work_version"
// optional - RxJava2 support
implementation "androidx.work:work-rxjava2:$work_version"
// optional - GCMNetworkManager support
implementation "androidx.work:work-gcm:$work_version"
// optional - Test helpers
androidTestImplementation "androidx.work:work-testing:$work_version"
// optional - Multiprocess support
implementation "androidx.work:work-multiprocess:$work_version"
}
第二步,自定义Worker
这里我们需要定义,这个Task需要做什么,创建一个自定义Worker类,这里我们创建一个UploadWorker用于做一些后台上传的操作
class UploadWorker(appContext: Context, workerParams: WorkerParameters):
Worker(appContext, workerParams) {
override fun doWork(): Result {
// 做一些上传操作
uploadImages()
// 代表任务执行成功
return Result.success()
}
}
在doWork中我们可以写上任务需要执行的代码,当任务结束后需要返回一个Result,这个Result有三个值
Result.success() 任务执行成功
Result.failure() 任务执行失败
Result.retry() 任务需要重试
第三步, 创建一个WorkRequest
当定义完需要做什么后我们需要创建一个WorkRequest去启动这个任务的执行。WorkManager提供了很多灵活的API用于定义任务的启动逻辑,例如是否执行一次还是周期性执行,它的约束条件是什么等。这里演示我们使用OneTimeWorkRequest.
val uploadWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<UploadWorker>()
.build()
第四步, 提交WorkRequest
当创建完成WorkRequest,我们需要把它交给WorkManager去执行
WorkManager
.getInstance(myContext)
.enqueue(uploadWorkRequest)
进阶
一次性任务
创建一个简单的一次性任务
val myWorkRequest = OneTimeWorkRequest.from(MyWork::class.java)
如果需要增加一些配置如约束等可以使用builder
val uploadWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<MyWork>()
// 添加额外配置
.build()
加急工作
WorkManager 执行重要的工作,同时让系统更好地控制对资源的访问。
加急工作具有以下特点:
重要性:加急工作适合对用户重要或由用户启动的任务。
速度:加急工作最适合立即开始并在几分钟内完成的短任务。
配额:限制前台执行时间的系统级配额决定加急作业是否可以启动。
电源管理:电源管理限制(例如省电模式和打瞌睡模式)不太可能影响加急工作。
启动加急工作的方式也非常简单,可以直接调用setExpedited()设置该WorkRequest为一个加急任务
val request = OneTimeWorkRequestBuilder()
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build()
WorkManager.getInstance(context)
.enqueue(request)
这里setExpedited会有一个参数OutOfQuotaPolicy,代表系统配额不足时候,把该任务作为一个普通任务对待.
周期性任务
我们有一些需求例如,备份应用数据,上传日志,下载一些应用配置,需要周期性进行,我们可以定义PeriodicWorkRequest去创建周期性的任务.
val saveRequest =
PeriodicWorkRequestBuilder<SaveImageToFileWorker>(1, TimeUnit.HOURS)
.build()
上面这段代码每一小时执行一次任务.
但是这个时间约束也不是固定的,这里定义的时间实际上是最小间隔时间,系统会根据当前系统的情况进行适当调整。
我们还可以定义flexInterval让间隔提前一点
val myUploadWork = PeriodicWorkRequestBuilder<SaveImageToFileWorker>(
1, TimeUnit.HOURS, // repeatInterval
15, TimeUnit.MINUTES) // flexInterval
.build()
这样我们执行任务的时间是repeatInterval - flexInterval,上面代码的任务会在1小时-15分钟的时候执行.
周期性任务遇上约束条件
当周期性任务遇到一些约束条件不满足的时候将会延迟执行,直到约束条件满足.
关于约束
WorkManager提供了下列的一些约束条件.
NetworkType 网络条件约束,例如只能在连接WIFI的情况下执行.
BatteryNotLow 非电量低约束,在有充足的电量的时候执行.
RequiresCharging 需要充电的时候执行约束
DeviceIdle 在设备无状态时候运行,这样不会对设备的效率产生影响.
StorageNotLow 当设备有有足够的存储空间时候运行
创建一个约束使用Contraints.Builder()并赋值给WorkRequest.Builder().
下面代码展示创建一个约束该任务只会在wifi并且在充电的时候执行.
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.build()
val myWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<MyWork>()
.setConstraints(constraints)
.build()
延迟任务
如果你指定的任务没有约束或者约束已经满足,那么它会立即开始执行,如果想让它有个最少的延迟,可以指定一个最小的延迟执行时间.
下面这个例子展示设置最小10分钟后开始加入队列.
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setInitialDelay(10, TimeUnit.MINUTES)
.build()
上面展示的针对OneTimeWorkRequestBuilder同样也适用于PeriodicWorkRequest.
退避策略
如果一个任务失败返回Result.retry(), 你的任务可以在稍等进行重试,这种退避策略可以自定义,这里连个自定义的属性
退避延迟:退避延迟执行下次尝试任务的最少时间,通常我们自定义最少不能低于[MIN_BACKOFF_MILLIS]
退避策略:退避策略可以指定两种一个是LINEAR(线性)和EXPONENTIAL(幂等)
实际上每一个任务都有一个默认的退避策略,缺省的退避策略是EXPONENTIAL和30s的延迟,但是你可以自定义,下面是一个自定义的例子。
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build()
Tag任务
每一个任务都可以附加上一个标签,稍后可以使用这个标签找到该任务,取消它或者查看的执行进度. 如果你有一组任务,可以添加同一个标签,可以统一的操作它们。例如使用WorkManager.cancelAllWorkByTag(String)取消所有的任务,使用WorkManager.getWorkInfosByTag(String)返回一个任务信息列表查看当前任务的状态.
下面是一个展示给任务赋值一个标签
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.addTag("cleanup")
.build()
链式任务
WorkManager还提供了一种定义顺序执行任务或者并发执行任务的方式。
使用这种方式创建任务通过
WorkManager.beginWith(OneTimeWorkRequest)
WorkManager.beginWith(List<OneTimeWorkRequest>)
以上两个方式都会返回一个WorkContinuation.
WorkContinuation随后可以继续调用
then(OneTimeWorkRequest)
then(List<OneTimeWorkRequest>)
执行链式任务
最后可以调用enqueue()去执行你的工作链. 举个例子
WorkManager.getInstance(myContext)
.beginWith(listOf(plantName1, plantName2, plantName3))
.then(cache)
.then(upload)
.enqueue()
Input Mergers
一个父任务的执行结果可以传递给子任务,例如上面plantName1, plantName2, plantName3执行的结果可以传递
给cache任务,WorkManager使用InputMerger去管理这些多个父任务的输出结果到子任务.
这里有两种不同类型的的InputMerger
OverwritingInputMerger 从输入到输出增加所有的key,遇到冲突的情况,覆盖之前的key的值
ArrayCreatingInputMerger 从输入到输出增加所有的key,遇到冲突的情况进行创建数组
工作链的状态
当前面的任务阻塞住的时候后面的任务同样也是阻塞状态.
当前面的任务执行成功后,后面的任务才能继续开始执行
当一个任务失败进行重试的时候并不会影响并发的任务
一个任务失败,后面的任务也会是失败状态
对于取消也是这样
结语
总的来说WorkManager是一个非常好用的组件,它解决了一些曾经实现起来比较繁琐的功能,例如它的约束执行,我们可以等待有网络时候执行任务。我们利用周期性执行任务功能能够很方便的执行一些诸如刷新token, 定期日志上传等功能.
链接:https://juejin.cn/post/7247404852945944635
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。