后台工作利器-WorkManager(Part1)

200 阅读8分钟

workmanager_main.svg

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情

本来这篇是准备直接跟大家分享WorkManager在nowinandroid项目的使用的。但是考虑到nowinandroid项目目前也只是一个Google为了演示现代android开发技术的demo项目,很多地方没有完全展现出库的能力和牛X的地方。所以为了不让新人低估了有些库的强大之处,后面会把我个人认为牛x的库单独拿出来给大家介绍,相信在大家对库有个全面的了解后,再拿出nowinandroid的使用例子来给大家看,不用多说大家应该也能很快的理解,达到水到渠成的效果!

我们经常会想当然的认为越是强大的库越是难以理解。其实真不是啊,其实牛x的库之所以🐂的地方,往往是因为它们在解决我们开发中痛点的时候,提供的反而是最简单最好理解的解决思路。所以在大家开始往下看之前完全可以丢掉学习成本的顾虑,可以说只要你是个正经人,花个半天时间,完全可以掌握对今天要讲的WorkManager库的所有内容。

1. 三要素-WorkManager、WorkRequest、 Worker

假设我们是公司老板要赚钱,赚钱需要做事,做事就需要工人(worker)。工人做事需要知道具体做什么,什么时候做,做几次,啥条件下不做了等等工作细节(work request)才能开始工作吧?老板不可能永远在公司(前台)同时等着每个工人做每件事吧?那么我们还需要聘请管理者(work manager)来帮我们监督观察工人来做事。然后要求他们随时把工作情况汇报给在外面(后台甚至app进程已经被杀死)的我们。

上面的流程应该每个公司都差不多吧。没啥难理解的地方吧!WorkManager就是这个帮助我们管理公司的角色。我们只用把work request下发给work manager,然后work manager安排worker就去按照找我们下发的具体要求(包含在work request里面)去做事了。关心的话,我们可以随时让负责的work manager给我们汇报相关的工作情况 ;否则就按照我们下发的内容来。

所以要搞掌握WorkManager库,弄清楚这三个角色就可以了。下面我们来一个个的拆开来讲。

2.Worker

工作是工人来做的。首先我们看看如何定义一个Worker。

扩展 Worker 类并替换 doWork() 方法。例如,如需创建上传图像的 Worker,您可以执行以下操作:

//负责做doWork方法里面具体工作的工人类
class UploadWorker(appContext: Context, workerParams: WorkerParameters):
       Worker(appContext, workerParams) {
   override fun doWork(): Result {

       // 上传图片
       uploadImages()

       // 返回结果
       return Result.success()
   }
}

从 doWork() 返回的 Result 会通知 WorkManager 服务工作是否成功,以及工作失败时是否应重试工作。

  • Result.success():工作成功完成。
  • Result.failure():工作失败。
  • Result.retry():工作失败,应根据其重试政策在其他时间尝试。

3.WorkRequest工作请求

这里大家可以直接参考官网的文档定义工作请求  |  Android 开发者  |  Android Developers (google.cn),下面的内容是参考官网的文档做的总结,节省时间大家可以直接看本文的总结。

工作请求以下面几点组成:

  • 工作周期

    • 仅仅一次
      //方式1 静态方法 from构建,适用于不需要啥配置的工作
      val myWorkRequest = OneTimeWorkRequest.from(MyWork::class.java)
      
      //方式2 builder构建器构建,适用于带额外配置的工作
      val myWorkRequest =OneTimeWorkRequestBuilder<MyWork>()
         // 可以添加额外配制
         .build()
      
    • 周期性多次
      //指定周期后根据情况执行工作。注意这里会受当前手机内存占用,电量等等因素影响执行实际工作的具体时间
      //,并不一定是1小时后立马执行!
      val myWorkRequest =
             PeriodicWorkRequestBuilder<SaveImageToFileWorker>(1, TimeUnit.HOURS)
          // 可以添加额外配置
                 .build()
      
      //指定的重复间隔-弹性间隔等于执行工作的时间窗口。例子里面就是45分钟-60分钟都有可能执行工作!
      //如果需求工作必须在1小时内完成,那么这种方式比较适合!
      val myWorkRequest = PeriodicWorkRequestBuilder<SaveImageToFileWorker>(
             1, TimeUnit.HOURS, // 重复间隔
             15, TimeUnit.MINUTES) // 弹性间隔
          .build()
      
    • 延迟后开始一次或周期多次

      如果工作没有约束,或者当工作加入队列时所有约束都得到了满足,那么系统可能会选择立即运行该工作。如果您不希望工作立即运行,可以将工作指定为在经过一段最短初始延迟时间后再启动。

      下面举例说明了如何将工作设置为在加入队列后至少经过 10 分钟后再运行。

      val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
         .setInitialDelay(10, TimeUnit.MINUTES)
         .build()
      

      该示例说明了如何为 OneTimeWorkRequest 设置初始延迟时间,您也可以为 PeriodicWorkRequest 设置初始延迟时间。在这种情况下,定期工作只有首次运行时会延迟。

  • 工作优先级

    • 加急工作
      val request = OneTimeWorkRequestBuilder()
          .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
          .build()
      
      这里setExpedited方法需要传参一个配额外政策。简单说就是如果当前加急工作实行条件不符合配额条件我们预期这个工作怎么执行。OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST代表当做非加急工作执行;OutOfQuotaPolicy.DROP_WORK_REQUEST代表如果不能加急那么就放弃此次工作。所以我们在加急的时候可以根据实际情况来传参了。
  • 工作约束

    类型说明
    NetworkType约束运行工作所需的网络类型
    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()
    

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

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

  • 重试和退避政策

    如果您需要让 WorkManager 重试工作,可以从工作器返回 Result.retry()。然后,系统将根据退避延迟时间退避政策重新调度工作。

    val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
       .setBackoffCriteria(
           BackoffPolicy.LINEAR,
           OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
           TimeUnit.MILLISECONDS)
       .build()
    

    在本示例中,最短退避延迟时间设置为允许的最小值,即 10 秒。由于政策为 LINEAR,每次尝试重试时,重试间隔都会增加约 10 秒。例如,第一次运行以 Result.retry() 结束并在 10 秒后重试;然后,如果工作在后续尝试后继续返回 Result.retry(),那么接下来会在 20 秒、30 秒、40 秒后重试,以此类推。如果退避政策设置为 EXPONENTIAL,那么重试时长序列将接近 20、40、80 秒,以此类推。

  • 标记工作

    每个工作请求都有一个唯一标识符,该标识符可用于在以后标识该工作,以便取消工作或观察其进度。 如果有一组在逻辑上相关的工作,对这些工作项进行标记可能也会很有帮助。通过标记,您一起处理一组工作请求。例如,WorkManager.cancelAllWorkByTag(String) 会取消带有特定标记的所有工作请求,WorkManager.getWorkInfosByTag(String) 会返回一个 WorkInfo 对象列表,该列表可用于确定当前工作状态。

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

    val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
       .addTag("cleanup")
       .build()
    
  • 分配数据

      // 工作中需要数据
      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()
         }
         ...
      }
    
      // 创建工作请求的时候存入工作时需要用到的数据
      val myUploadWork = OneTimeWorkRequestBuilder<UploadWork>()
          //存数据
         .setInputData(workDataOf(
             "IMAGE_URI" to "http://..."
         ))
         .build()
         
    

4.小结

本文是介绍WorkManager库的第一章,主要是希望通过生活中的例子让大家明白此库的作用和主要组成。我们已经介绍了做事的角色-工人(woker),工作请求(work request)。那么下一章我们来介绍work manager,并把这3者结合起来展示一个完整的创建和使用流程,来把第一章讲的通过例子融会贯通。

水平有限!写作不易,欢迎大家指出不足,共同进步。也希望点赞收藏来支持我~

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情