• WorkManager学习一


    一、前言

    目前执行长期的后台任务时候,官方推荐使用WorkManager来处理后台任务,这里对WorkManager进行一个简单的记录。WorkManager主要可以执行以下几种任务
    在这里插入图片描述

    二、添加依赖

    dependencies {
        val work_version = "2.7.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"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    三、简单示例

    一个WorkManager需要三部分组成。定义一个Work,创建一个WorkRequest,提交给WorkManager进行执行。以下是一个简单的一次性任务的示例。

    WorkTest.kt

    class WorkTest(val appContext: Context, workerParams: WorkerParameters): Worker(appContext, workerParams) {
        override fun doWork(): Result {
            Log.e("YM--->","后台任务执行中,任务线程:${Thread.currentThread().id}-->线程名字:${Thread.currentThread().name}")
            return Result.success()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    以下是两种创建WorkRequest的方式,一种复杂的,一种简单的,根据情况选择一种实现。

        private fun initWork() {
    //        val uploadWorkRequest: WorkRequest = //复杂的构建器方式
    //            OneTimeWorkRequestBuilder<WorkTest>()
    //                .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)//设置加急任务
    //                .build()
            val uploadWorkRequest: WorkRequest = OneTimeWorkRequest.from(WorkTest::class.java)//简单方式
            WorkManager
                .getInstance(this)
                .enqueue(periodicWorkRequest)
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用WorkManager进行执行

    WorkManager
                .getInstance(this)
                .enqueue(periodicWorkRequest) 
    
    • 1
    • 2
    • 3

    四、定期任务

    定期任务可以多次执行一个任务。以下是创建方式

    val saveRequest =
           PeriodicWorkRequestBuilder<WorkTest>(1, TimeUnit.HOURS)
        // Additional configuration
               .build()
    
    • 1
    • 2
    • 3
    • 4

    需要注意下时间,如果时间小于15分钟,则会按照十五分钟进行计算,如果大于十五分钟则按照实际时间计算。

    假设想要定义一个灵活的时间段,比如最后十五分钟执行任务,可以使用以下方式

    
    val myUploadWork = PeriodicWorkRequestBuilder<SaveImageToFileWorker>(
           1, TimeUnit.HOURS, // repeatInterval (the period cycle)
           15, TimeUnit.MINUTES) // flexInterval
        .build()
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在创建 PeriodicWorkRequest 时传递 flexInterval 以及 repeatInterval。灵活时间段从 repeatInterval - flexInterval 开始,一直到间隔结束。

    重复间隔必须大于或等于 PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS,而灵活间隔必须大于或等于 PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS

    五、工作约束

    这里参考官方网站:

    约束可确保将工作延迟到满足最佳条件时运行。以下约束适用于 WorkManager。

    NetworkType约束运行工作所需的网络类型。例如 Wi-Fi ([UNMETERED](https://developer.android.google.cn/reference/androidx/work/NetworkType?hl=zh-cn#UNMETERED))。
    BatteryNotLow如果设置为 true,那么当设备处于“电量不足模式”时,工作不会运行。
    RequiresCharging如果设置为 true,那么工作只能在设备充电时运行。
    DeviceIdle如果设置为 true,则要求用户的设备必须处于空闲状态,才能运行工作。在运行批量操作时,此约束会非常有用;若是不用此约束,批量操作可能会降低用户设备上正在积极运行的其他应用的性能。
    StorageNotLow如果设置为 true,那么当用户设备上的存储空间不足时,工作不会运行。

    如需创建一组约束并将其与某项工作相关联,请使用一个 Contraints.Builder() 创建 Constraints 实例,并将该实例分配给 WorkRequest.Builder()

    例如,以下代码会构建了一个工作请求,该工作请求仅在用户设备正在充电且连接到 Wi-Fi 网络时才会运行:

    val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .setRequiresCharging(true)
       .build()
    
    val myWorkRequest: WorkRequest =
       OneTimeWorkRequestBuilder<MyWork>()
           .setConstraints(constraints)
           .build()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果指定了多个约束,工作将仅在满足所有约束时才会运行。

    如果在工作运行时不再满足某个约束,WorkManager 将停止工作器。系统将在满足所有约束后重试工作。

    六、延迟工作

    上面的任务默认来说会立即执行,倘若想要延迟执行的话可以采取以下方式:

    
    val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
       .setInitialDelay(10, TimeUnit.MINUTES)
       .build()
    
    • 1
    • 2
    • 3
    • 4

    需要注意的是定时任务,只有首次会延迟。

    七、 重试和重试策略

    倘若任务执行失败,想要进行重试,可以使用该方式。重试有两个参数构成:重试时间和重试策略。重试时间是指每次重试前的等待时间,最短10s或者 MIN_BACKOFF_MILLIS。重试策略则是指后续的重试时间以何种方式增涨,有两个参数,一个是线性增涨LINEAR,一个是指数增长EXPONENTIAL。不能设置每次固定的时间。如果使用重试的话,需要在定义 Worker时候返回Result.retry(),不能返回Result.success()。以下是参考代码:

    val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
       .setBackoffCriteria(
           BackoffPolicy.LINEAR,
           OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
           TimeUnit.MILLISECONDS)
       .build()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    八、工作标识

    以下参考官网网站:

    每个工作请求都可以设置一个唯一标识符,该标识符可用于在以后标识该工作,以便取消工作或观察其进度

    如果有一组在逻辑上相关的工作,对这些工作项进行标记可能也会很有帮助。通过标记,您一起处理一组工作请求。

    例如,WorkManager.cancelAllWorkByTag(String) 会取消带有特定标记的所有工作请求,WorkManager.getWorkInfosByTag(String)会返回一个 WorkInfo 对象列表,该列表可用于确定当前工作状态。

    以下代码展示了如何向工作添加“cleanup”标记:

    val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
       .addTag("cleanup")
       .build()
    
    • 1
    • 2
    • 3

    最后,可以向单个工作请求添加多个标记。这些标记在内部以一组字符串的形式进行存储。您可以使用 WorkInfo.getTags() 获取与 WorkRequest 关联的标记集。

    Worker 类中,您可以通过 ListenableWorker.getTags() 检索其标记集。

    九、传递参数

    有些工作需要传递一些参数。可以使用以下的方式, 示例代码如下:

    
    // Define the Worker requiring input
    class UploadWork(appContext: Context, workerParams: WorkerParameters)
       : Worker(appContext, workerParams) {
    
       override fun doWork(): Result {
           val imageUriInput =
               inputData.getString("IMAGE_URI") ?: return Result.failure()
    
           uploadFile(imageUriInput)
           return Result.success()
       }
       ...
    }
    
    // Create a WorkRequest for your Worker and sending it input
    val myUploadWork = OneTimeWorkRequestBuilder<UploadWork>()
       .setInputData(workDataOf(
           "IMAGE_URI" to "http://..."
       ))
       .build()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    十、加急任务

    加急任务据官网说是对那些需要立即执行的任务,比如添加订阅,支付账单,发送消息。但是初步接触,没有发现和其它任务的区别。可能是在WorkManager有很多任务时候,会导致任务排队,加急任务可以将任务插入到队前进行加快执行。

    加急任务是在WorkManager2.7的时候添加的。并且可能在Android12之前的版本上运行前台服务。所以在一定程度上限制了其使用场景。

    以下是引用内容

    在 Android 12 之前,工作器中的 getForegroundInfoAsync()getForegroundInfo() 方法可让 WorkManager 在您调用 setExpedited() 时显示通知。

    如果您想要请求任务作为加急作业运行,则所有的 ListenableWorker 都必须实现 getForegroundInfo 方法。

    注意:如果未能实现对应的 getForegroundInfo 方法,那么在旧版平台上调用 setExpedited 时,可能会导致运行时崩溃。

    以 Android 12 或更高版本为目标平台时,前台服务仍然可通过对应的 setForeground 方法使用。

    注意setForeground() 可能会在 Android 12 上抛出运行时异常,并且在启动受到限制时可能会抛出异常。

    工作器

    工作器不知道自身所执行的工作是否已加急。不过,在某些版本的 Android 上,如果 WorkRequest 被加急,工作器可以显示通知。

    为此,WorkManager 提供了 getForegroundInfoAsync() 方法,您必须实现该方法,让 WorkManager 在必要时显示通知,以便启动 ForegroundService

    CoroutineWorker

    如果您使用 CoroutineWorker,则必须实现 getForegroundInfo()。然后,在 doWork() 内将其传递给 setForeground()。这样做会在 Android 12 之前的版本中创建通知。

    请参考以下示例:

      class ExpeditedWorker(appContext: Context, workerParams: WorkerParameters):   CoroutineWorker(appContext, workerParams) {   override suspend fun getForegroundInfo(): ForegroundInfo {       return ForegroundInfo(           NOTIFICATION_ID, createNotification()       )   }   override suspend fun doWork(): Result {       TODO()   }    private fun createNotification() : Notification {       TODO()    }}
    
    • 1

    注意:您应该将 setForeground() 封装在 try/catch 块中,以捕获可能出现的 IllegalStateException。如果您的应用此时无法在前台运行,便可能会发生这类异常。在 Android 12 及更高版本中,您可以使用更详细的 ForegroundServiceStartNotAllowedException

    配额政策

    您可以控制当应用达到其执行配额时加急工作会发生什么情况。如需继续,您可以传递 setExpedited()

    • OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST,这会导致作业作为普通工作请求运行。上述代码段演示了此操作。
    • OutOfQuotaPolicy.DROP_WORK_REQUEST,这会在配额不足时导致请求取消。

    示例应用

    如需查看关于 WorkManager 2.7.0 如何使用加急工作的完整示例,请查看 GitHub 上的 WorkManagerSample

    延迟加急工作

    系统会尝试在调用指定的加急作业后,尽快执行该作业。不过,与其他类型的作业一样,系统可能会延迟启动新的加急工作,如在以下情况下:

    • 负载:系统负载过高,当有过多作业已在运行或者当系统内存不足时,就会发生这种情况。
    • 配额:已超出加急作业配额限制。加急工作使用基于应用待机存储分区的配额系统,并限制滚动时间窗口中的最大执行时间。用于加急工作的配额比用于其他类型的后台作业的配额限制性更强。

    需要注意的是,上文继承的Work类,是ListenableWorker的子类,所以依然要重写getForegroundInfoAsync()函数。

    十一、参考链接

    1. WorkManager 使用入门  |  Android 开发者  |  Android Developers

    2. 应用架构:数据层 - 使用 WorkManager 调度任务 - Android 开发者  |  Android Developers

  • 相关阅读:
    Kotlin File writeText appendText appendBytes readBytes readText
    网络安全-防火墙安全加固
    2021 年全国职业院校技能大赛高职组“信息安全管理与评估”赛项 A 卷 第一阶段任务书
    【Arma3脚本教程】一、基本介绍
    分享一个爬虫数据挖掘 农村产权交易数据可视化平台 数据分析大数据 Java、python双版(源码、调试、LW、开题、PPT)
    禁用token及无感知更新token功能实现
    Sequential Recommendation with Graph Neural Networks
    怎么样可以算一名云原生工程师?
    MATLAB解析和保存ini文件
    Linux学习笔记——文件权限的修改
  • 原文地址:https://blog.csdn.net/Mr_Tony/article/details/125600406