当前位置: 首页 > news >正文

Android Jetpack组件之WorkManager后台任务管理的介绍与使用(二)

一、介绍

通过上一篇文,Android Jetpack组件之WorkManager后台任务管理的介绍与使用(一)_蜗牛、Z的博客-CSDN博客

我们可以弄清楚workmanager从接入到使用的基本流程。基本可以满足我们日常。那只是简单的入门。如果遇到更复杂的功能,那简单的就无法满足。

二、管理进阶

单一执行:

WorkManager.getInstance(requireContext()).enqueue(myWork)

唯一:

唯一工作既可用于一次性工作,也可用于定期工作。您可以通过调用以下方法之一创建唯一工作序列,具体取决于您是调度重复工作还是一次性工作

  • WorkManager.enqueueUniqueWork()(用于一次性工作)
    这个只支持OneTimeWorkRequest
     
  • WorkManager.enqueueUniquePeriodicWork()(用于定期工作)
    只支持PeriodicWorkRequest
     

这两种方法都接受 3 个参数:

  • uniqueWorkName - 用于唯一标识工作请求的 String
  • existingWorkPolicy - 此 enum 可告知 WorkManager:如果已有使用该名称且尚未完成的唯一工作链,应执行什么操作。如需了解详情,请参阅冲突解决政策。
  • work - 要调度的 WorkRequest

冲突解决政策

对于一次性工作,您需要提供一个 ExistingWorkPolicy,它支持用于处理冲突的 4 个选项。

  • REPLACE:用新工作替换现有工作。此选项将取消现有工作。
  • KEEP:保留现有工作,并忽略新工作。
  • APPEND:将新工作附加到现有工作的末尾。此政策将导致您的新工作链接到现有工作,在现有工作完成后运行。

现有工作将成为新工作的先决条件。如果现有工作变为 CANCELLED 或 FAILED 状态,新工作也会变为 CANCELLED 或 FAILED。如果您希望无论现有工作的状态如何都运行新工作,请改用 APPEND_OR_REPLACE

  • APPEND_OR_REPLACE 函数类似于 APPEND,不过它并不依赖于先决条件工作状态。即使现有工作变为 CANCELLED 或 FAILED 状态,新工作仍会运行。

对于定期工作,您需要提供一个 ExistingPeriodicWorkPolicy,它支持 REPLACE 和 KEEP 这两个选项。这些选项的功能与其对应的 ExistingWorkPolicy 功能相同。

三、如何获取worker对象

        在将工作加入队列后,您可以随时按其 nameid 或与其关联的 tag 在 WorkManager 中进行查询,以检查其状态

三种:

//by uuid
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>

监听器:

利用每个方法的 LiveData 变种,您可以通过注册监听器来观察 WorkInfo 的变化

   val resultData = WorkManager.getInstance(application).getWorkInfoByIdLiveData(id)resultData.observe(this) {if (it?.state == WorkInfo.State.SUCCEEDED) {//}}

复杂的work查询:WorkQuery

        WorkManager 2.4.0 及更高版本支持使用 WorkQuery 对象对已加入队列的作业进行复杂查询。WorkQuery 支持按工作的标记、状态和唯一工作名称的组合进行查询。

        val workQuery = WorkQuery.Builder.fromTags(listOf("one1")).addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED)).addUniqueWorkNames(listOf("preProcess", "sync")).build()val workInfos = WorkManager.getInstance(application).getWorkInfos(workQuery)

解释:

WorkQuery 中的每个组件(标记、状态或名称)与其他组件都是 AND 逻辑关系。组件中的每个值都是 OR 逻辑关系

例如:(name1 OR name2 OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...)

四、取消和停止work

// by id
workManager.cancelWorkById(build.id)// by name
workManager.cancelUniqueWork("sync")// by tag
workManager.cancelAllWorkByTag("syncTag")

五、停止正在运行的WORK

正在运行的 Worker 可能会由于以下几种原因而停止运行:

  • 您明确要求取消它(例如,通过调用 WorkManager.cancelWorkById(UUID) 取消)。
  • 如果是唯一工作,您明确地将 ExistingWorkPolicy 为 REPLACE 的新 WorkRequest 加入到了队列中。旧的 WorkRequest 会立即被视为已取消。
  • 您的工作约束条件已不再满足。
  • 系统出于某种原因指示您的应用停止工作。如果超过 10 分钟的执行期限,可能会发生这种情况。该工作会调度为在稍后重试。

在您的工作器停止后,WorkManager 会立即调用 ListenableWorker.onStopped(),您可以调用 ListenableWorker.isStopped() 方法以检查工作器是否已停止

观察worker的中间进度

        WorkManager 为设置和观察工作器的中间进度添加了一流的支持。如果应用在前台运行时,工作器保持运行状态,那么也可以使用返回 WorkInfo 的 LiveData 的 API 向用户显示此信息

        只有在 ListenableWorker 运行时才能观察到和更新进度信息。如果尝试在 ListenableWorker 完成执行后在其中设置进度,则将会被忽略。您还可以使用 getWorkInfoBy…() 或 getWorkInfoBy…LiveData() 方法来观察进度信息。这两个方法会返回 WorkInfo 的实例,后者有一个返回 Data 的新 getProgress() 方法

六、更新进度

        对于使用 ListenableWorker 或 Worker 的 Java 开发者,setProgressAsync() API 会返回 ListenableFuture<Void>;更新进度是异步过程,因为更新过程涉及将进度信息存储在数据库中。在 Kotlin 中,您可以使用 CoroutineWorker 对象的 setProgress() 扩展函数来更新进度信息

class MyCoroutineWorker(appContext: Context,params: WorkerParameters
) : CoroutineWorker(appContext, params) {companion object {const val Progress = "Progress"private const val delayDuration = 1L}override suspend fun doWork(): Result {val firstUpdate = workDataOf(Progress to 0)val lastUpdate = workDataOf(Progress to 100)setProgress(firstUpdate)delay(delayDuration)setProgress(lastUpdate)return Result.Success.success()}override suspend fun getForegroundInfo(): ForegroundInfo {val id = Random.nextInt(0, Int.MAX_VALUE)return ForegroundInfo(id, getNotion())}private fun getNotion(): Notification {val notification = Notification()return notification;}
}

观察信息:

   val liveData =WorkManager.getInstance(application).getWorkInfoByIdLiveData(coroutineWorker.id)liveData.observe(this, Observer { workinfo: WorkInfo? ->if (workinfo != null) {val datta = workinfo.progressval state = workinfo.stateif (datta != null && datta.size() > 0) {}}})

注意:

workDataOf(Progress to 0):

workDataOf是Data类里面的,workDataOf直接返回data对象,有因为这个data的存储是Map,所以Progress to 0=map(key,value)=map(Progress ,0)

在LiveData的observe(this)中,it.progress是Data对象,直接通过map对象去获取

加入队列:

     val  coroutineWorker= PeriodicWorkRequestBuilder<MyCoroutineWorker>(20, TimeUnit.MINUTES).build()WorkManager.getInstance(application).enqueue(coroutineWorker)

七、链接work

        可以使用 WorkManager 创建工作链并将其加入队列。工作链用于指定多个依存任务并定义这些任务的运行顺序。当您需要以特定顺序运行多个任务时

        如需创建工作链,您可以使用 WorkManager.beginWith(OneTimeWorkRequest) 或 WorkManager.beginWith(List<OneTimeWorkRequest>),这会返回 WorkContinuation 实例。

        然后,可以使用 WorkContinuation 通过 then(OneTimeWorkRequest) 或 then(List<OneTimeWorkRequest>) 添加 OneTimeWorkRequest 依赖实例。每次调用 WorkContinuation.then(...) 都会返回一个新的 WorkContinuation 实例。如果添加了 OneTimeWorkRequest 实例的 List,这些请求可能会并行运行

        最后,您可以使用 WorkContinuation.enqueue() 方法对 WorkContinuation 工作链执行 enqueue() 操作

如下:

val work=  OneTimeWorkRequest.from(MyWorks::class.java)WorkManager.getInstance(application).beginWith(work);WorkManager.getInstance(application).beginWith(work).then(work).then(work);

这种顺序类似属性动画一样,后面按顺序执行。

八、输入合并器

当您链接 OneTimeWorkRequest 实例时,父级工作请求的输出将作为子级的输入传入。因此,在上面的示例中,plantName1plantName2 和 plantName3 的输出将作为 cache 请求的输入传入。

WorkManager 提供两种不同类型的 InputMerger

  • OverwritingInputMerger 会尝试将所有输入中的所有键添加到输出中。如果发生冲突,它会覆盖先前设置的键。

  • ArrayCreatingInputMerger 会尝试合并输入,并在必要时创建数组

OverwritingInputMerger

OverwritingInputMerger 是默认的合并方法。如果合并过程中存在键冲突,键的最新值将覆盖生成的输出数据中的所有先前版本。如果每种植物的输入都有一个与其各自变量名称("plantName1""plantName2" 和 "plantName3")匹配的键,传递给 cache 工作器的数据将具有三个键值对。如果存在冲突,那么最后一个工作器将在争用中“取胜”,其值将传递给 cache。

由于工作请求是并行运行的,因此无法保证其运行顺序。在上面的示例中,plantName1 可以保留值 "tulip" 或 "elm",具体取决于最后写入的是哪个值。如果有可能存在键冲突,并且您需要在合并器中保留所有输出数据,那么 ArrayCreatingInputMerger 可能是更好的选择。

ArrayCreatingInputMerger

        将每个键与数组配对。如果每个键都是唯一的,您会得到一系列一元数组,如果存在任何键冲突,那么所有对应的值会分组到一个数组中。

久、链接和work状态

        要工作成功完成(即,返回 Result.success()),OneTimeWorkRequest 链便会按顺序执行。运行时,工作请求可能会失败或被取消,这会对依存工作请求产生下游影响。

        当第一个 OneTimeWorkRequest 被加入工作请求链队列时,所有后续工作请求会被屏蔽,直到第一个工作请求的工作完成为止。

 

在加入队列且满足所有工作约束后,第一个工作请求开始运行。如果工作在根 OneTimeWorkRequest 或 List<OneTimeWorkRequest> 中成功完成(即返回 Result.success()),系统会将下一组依存工作请求加入队列。

 

        如果该重试政策未定义或已用尽,或者您以其他方式已达到 OneTimeWorkRequest 返回 Result.failure() 的某种状态,该工作请求和所有依存工作请求都会被标记为 FAILED。

OneTimeWorkRequest 被取消时遵循相同的逻辑。任何依存工作请求也会被标记为 CANCELLED,并且无法执行其工作。

请注意:

        如果要向已失败或已取消工作请求的链附加更多工作请求,新附加的工作请求也会分别标记为 FAILED 或 CANCELLED。如果您想扩展现有链的工作,请参阅 ExistingWorkPolicy 中的 APPEND_OR_REPLACE

十一、调试 WorkManager

启用日志记录

        如需确定工作器未正确运行的原因,查看详细的 WorkManager 日志很有帮助。如需启用日志记录功能,您需要使用自定义初始化。首先,通过创建应用了清单合并规则 remove 的新 WorkManager 提供程序来停用 AndroidManifest.xml 中的默认 WorkManagerInitializer(2.6以后用InitializationProvider)

引入:

<providerandroid:name="androidx.startup.InitializationProvider"android:authorities="${applicationId}.androidx-startup"tools:node="remove">

现在,默认 WorkManager 初始化程序已停用,您可以使用按需初始化。为此,android.app.Application 类必须提供 androidx.work.Configuration.Provider 的实现。

class MyApplication() : Application(), Configuration.Provider {override fun getWorkManagerConfiguration() =Configuration.Builder().setMinimumLoggingLevel(android.util.Log.DEBUG).build()
}Java:public Configuration getWorkManagerConfiguration() {Configuration.Builder builder= new Configuration.Builder();builder.setMinimumLoggingLevel(android.util.Log.DEBUG);return builder.build();}

定义自定义 WorkManager 配置后,WorkManager 会在您调用 WorkManager.getInstance(Context) 时进行初始化,而不是在应用启动时自动初始化

​启用 DEBUG 日志记录后,系统会开始显示更多包含日志标记前缀 WM- 的日志

从 WorkManager 2.4.0 及更高版本请求诊断信息

在应用的调试 build 中,您可以使用以下命令从 WorkManager 2.4.0 及更高版本请求诊断信息:

adb shell am broadcast -a "androidx.work.diagnostics.REQUEST_DIAGNOSTICS" -p "<your_app_package_name>"

这提供了以下方面的信息:

  • 在过去 24 小时内完成的工作请求。
  • 目前正在运行的工作请求。
  • 预定运行的工作请求。

诊断信息如下所示(输出通过 logcat 显示)

http://www.lryc.cn/news/2759.html

相关文章:

  • 【MySQL】第十七部分 约束
  • java ssm集装箱码头TOS系统调度模块的设计与实现
  • MS14-064(OLE远程代码执行漏洞复现)
  • 【C++深陷】之shared_ptr
  • SpringMVC中遇到的错误
  • 姿态估计端到端新方案 | DirectMHP:用于全范围角度2D多人头部姿势估计
  • jvm学习的核心(五)---垃圾回收算法和常见垃圾回收器
  • 亿级高并发电商项目-- 实战篇 --万达商城项目 二(Zookeeper、Docker、Dubbo-Admin等搭建工作
  • 【C#基础】 C# 数据类型总结
  • 格子玻尔兹曼法介绍
  • 活动星投票在时间的河流上造园分组怎么设置如何进行分组报名
  • c#小笔记本-基础
  • DamiCMS SQL注入分析
  • 图傅里叶变换的推导和理解
  • Java八股文(Java面试题)
  • java ssm idea高校图书借阅管理系统设计2z87z
  • 电脑重装系统注册表恢复方法
  • 信道建模(大尺度、小尺度、莱斯衰落、瑞利衰落、莱斯信道、瑞利信道)
  • 2022年12月电子学会Python等级考试试卷(四级)答案解析
  • 通过实例告诉你lua中ipairs到底是怎么遍历的!
  • Axios异步请求 json格式
  • Postgresql源码(100)Portal与事务的关系(顶层事务与子事务)
  • Java、JSP企业快信系统的设计与实现
  • 1.2(完结)C语言进阶易忘点速记
  • 雅思经验(十一)
  • C++中的智能指针
  • LSTM已死,Transformer当立(LSTM is dead. Long Live Transformers! ):下
  • OJ万题详解––[NOIP2004 提高组] 合并果子(C++详解)
  • MySQL-字符集和比较规则
  • 微搭低代码从入门到精通12-网格布局