一、介绍

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

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

二、管理进阶

单一执行:


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


唯一:

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

这两种方法都接受 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对象

在将工作加入队列后,您可以随时按其 name id 或与其关联的 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.progress
                val state = workinfo.state
                if (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 实例时,父级工作请求的输出将作为子级的输入传入。因此,在上面的示例中, plantName1 plantName2 plantName3 的输出将作为 cache 请求的输入传入。

WorkManager 提供两种不同类型的 InputMerger

OverwritingInputMerger

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

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

ArrayCreatingInputMerger

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

久、链接和work状态

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

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

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

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

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

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

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

请注意:

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

十一、调试 WorkManager

启用日志记录

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

引入:

<provider
            android: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 显示)