Android WorkManager入门(二)

2024-01-19 20:36
文章标签 android 入门 workmanager

本文主要是介绍Android WorkManager入门(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

WorkManager入门

  • 上一篇
  • 前言
  • 创建 WorkRequest并提交 定时的任务(PeriodicWorkRequest)
  • 配合约束使用
  • 定义执行范围
  • 失败后的重试
  • 为WorkRequest打上TAG
    • 其他取消方法
  • 传参和返回参数
  • 总结
  • 参考资料


上一篇

Android WorkManager入门(一)

前言

在当今快节奏的生活中,移动设备已经成为我们日常工作和生活不可或缺的一部分。然而,随着应用程序的复杂性不断增加,开发人员面临着一个重要的挑战:如何在后台执行任务,而不会影响用户的体验和设备的性能?
在过去,开发人员通常使用传统的后台服务或定时任务来解决这个问题。然而,这些方法往往很复杂,需要大量的代码和资源,并且很难管理和调度任务。幸运的是,谷歌最近推出了一个新的解决方案:安卓WorkManager。
安卓WorkManager是一个灵活、强大的后台任务调度库,旨在帮助开发人员轻松管理和执行后台任务。它提供了一种简单的方式来调度任务,无论是一次性任务、定期任务还是延迟任务,都可以很容易地实现。同时,WorkManager还提供了一系列强大的功能,如任务链、约束条件和灵活的重试机制,以确保任务能够在最佳的时间和条件下执行。
在本文中,我们将深入探讨安卓WorkManager的原理和用法,并通过实际示例演示如何使用它来解决常见的后台任务问题。无论您是一名初学者还是一名有经验的开发人员,本文都将为您提供宝贵的知识和实用的技巧,帮助您更好地利用安卓WorkManager来优化您的应用程序。让我们一起开始这段关于安卓WorkManager的探索之旅吧!

好吧,不多BB,其实是因为安卓12以上想起后台服务必须要悬浮窗权限,想起还有个WorkManager这种东西,去官网学习然后总结一下。没有看入门一的可以先看一,或者看官网的也可以。

创建 WorkRequest并提交 定时的任务(PeriodicWorkRequest)

我们可能要定期备份数据、定期下载应用中的新鲜内容或者定期上传日志到服务器。就可以使用PeriodicWorkRequest

在使用之前我们除了接入依赖还需要新建一个worker

import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParametersclass MyWorker(appContext: Context, workerParams: WorkerParameters):Worker(appContext, workerParams) {companion object{private const val TAG = "MyWorker"}override fun doWork(): Result {Log.d(TAG, "doWork: 我正在做一些工作")return Result.success()}}

然后我们在需要使用的地方加上下例代码:

 val mWorkerRequest4 =PeriodicWorkRequestBuilder<MyWorker>(1, TimeUnit.HOURS).build()//通过WorkManager提交WorkRequest,执行MyWorkerWorkManager.getInstance(this).enqueue(mWorkerRequest4)

可以定义的最短重复间隔是 15 分钟(与 JobScheduler API 相同)。

然后我们运行项目,等待一小时…

在这里插入图片描述

ok,一分钟过去了,然后我们发现一跑起来其实就有日志输出了,所以这个时间其实是最小间隔时间,和setInitialDelay的延时是不一样的。

配合约束使用

这时我们加上一个约束,这时代码变成了下例这样:

	val constraints = Constraints.Builder().setRequiresCharging(true).build()val mWorkerRequest4 =PeriodicWorkRequestBuilder<MyWorker>(1, TimeUnit.HOURS).setConstraints(constraints).build()//通过WorkManager提交WorkRequest,执行MyWorkerWorkManager.getInstance(this).enqueue(mWorkerRequest4)

setRequiresCharging(true)标水充电时执行,如果不充电,那么这个定时任务就不会执行。不然就会像第一个例子一样,定时任务一开始就执行了。

定义执行范围

这是官方在每小时的最后 15 分钟内运行的定期工作的示例:

  //每小时的最后 15 分钟内运行的定期工作val mWorkerRequest5 =PeriodicWorkRequestBuilder<MyWorker>(1, TimeUnit.HOURS, // 执行周期15, TimeUnit.MINUTES) // 实际执行时间.build()//通过WorkManager提交WorkRequest,执行MyWorkerWorkManager.getInstance(this).enqueue(mWorkerRequest5)

结合这张图可以更好地理解两个时间在这里插入图片描述

RequiresApi最低为26

失败后的重试

后台任务有失败的可能,所以在Worker的doWork方法的返回值中给我们提供了三种方法,分别是:

  • Result.success()成功
  • Result.failure()失败
  • Result.retry()重试

成功和失败都是定性的,但是重试这个东西应该是会再执行的,那么怎么执行呢,我们看下面这个例子:

首先,我们将MyWorker改为下面这个样子:

import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParametersclass MyWorker(appContext: Context, workerParams: WorkerParameters):Worker(appContext, workerParams) {companion object{private const val TAG = "MyWorker"}override fun doWork(): Result {Log.d(TAG, "doWork: 我正在做一些工作")val doWorkFail = trueif (doWorkFail){Log.d(TAG, "doWork: 我失败了,重试一下")return Result.retry()}return Result.success()}}

然后再使用 setBackoffCriteria 方法对回退的工作进行处理:

  val mWorkerRequest6 = OneTimeWorkRequestBuilder<MyWorker>().setBackoffCriteria(BackoffPolicy.LINEAR,MIN_BACKOFF_MILLIS,TimeUnit.MILLISECONDS).build()//通过WorkManager提交WorkRequest,执行MyWorkerWorkManager.getInstance(this).enqueue(mWorkerRequest6 )

setBackoffCriteria 有一些参数,我们来看下都是什么

  • BackoffPolicy 指定回退时间策略;政策为 LINEAR,每次尝试重试时,重试间隔都会增加约 10 秒。例如,第一次运行以 Result.retry() 结束并在 10 秒后重试;然后,如果工作在后续尝试后继续返回 Result.retry(),那么接下来会在 20 秒、30 秒、40 秒后重试,以此类推。如果退避政策设置为 EXPONENTIAL,那么重试时长序列将接近 20、40、80 秒,以此类推。
    在这里插入图片描述
  • backoffDelay + timeUnit 具体的时间,最小为10s,在WorkRequest中有定义
    在这里插入图片描述
    我们跑起来看下效果:
    在这里插入图片描述
    结果如下
    在这里插入图片描述

为WorkRequest打上TAG

我们可以在使用中为WorkRequest打上Tag,这样就可以通过WorkManager.cancelAllWorkByTag(String) 取消带有特定标记的所有工作请求,或者用WorkManager.getWorkInfosByTag(String) 返回一个 WorkInfo 对象列表,该列表可用于确定当前工作状态。

来看下例代码:

 val mWorkerRequest7 = OneTimeWorkRequestBuilder<MyWorker>().addTag("myWorker").setInitialDelay(12, TimeUnit.MINUTES).build()
//通过WorkManager提交WorkRequest,执行MyWorkerWorkManager.getInstance(this).enqueue(mWorkerRequest7)Handler().postDelayed({val workInfosByTag = WorkManager.getInstance(this).getWorkInfosByTag("myWorker")Log.d(TAG, "testWorkManager: ${workInfosByTag.get()}")Log.d(TAG, "testWorkManager: ${WorkManager.getInstance(this).cancelAllWorkByTag("myWorker").result}")WorkManager.getInstance(this).cancelAllWorkByTag("myWorker")WorkManager.getInstance(this).cancelAllWorkByTag("myWorker1").state.observe(this) {Log.d(TAG, "testWorkManager: $it")}},10*1000L)

通过日志我们可以看到确实可以通过tag找到执行的work,但是并不能取消,还是执行了

2024-01-18 10:35:26.130 22739-22739 MainActivity            com.example.test                     D  testWorkManager: [WorkInfo{mId='17366cc4-fa74-4d42-98d9-9b4e845fb19e', mState=CANCELLED, mOutputData=Data {}, mTags=[myWorker, com.example.test.MyWorker], mProgress=Data {}}, WorkInfo{mId='4c902568-aab9-4a96-b7e5-eccc93eea99c', mState=CANCELLED, mOutputData=Data {}, mTags=[myWorker, com.example.test.MyWorker], mProgress=Data {}}, WorkInfo{mId='708826dd-8e63-44f2-9311-746b15140bac', mState=CANCELLED, mOutputData=Data {}, mTags=[myWorker, com.example.test.MyWorker], mProgress=Data {}}, WorkInfo{mId='79e954b7-9318-4d7c-bd67-b8fd0e810479', mState=SUCCEEDED, mOutputData=Data {}, mTags=[myWorker, com.example.test.MyWorker], mProgress=Data {}}, WorkInfo{mId='7fb339e0-6acc-4b64-b451-ec1c9b82aaa6', mState=CANCELLED, mOutputData=Data {}, mTags=[myWorker, com.example.test.MyWorker], mProgress=Data {}}, WorkInfo{mId='87938938-1fb9-4304-afd3-bff71716a187', mState=CANCELLED, mOutputData=Data {}, mTags=[myWorker, com.example.test.MyWorker], mProgress=Data {}}, WorkInfo{mId='9233f46d-0cd4-48d7-9d05-e504bf46d623', mState=ENQUEUED, mOutputData=Data {}, mTags=[myWorker, com.example.test.MyWorker], mProgress=Data {}}, WorkInfo{mId='a55eb79b-b0b0-4e84-8a46-436056c6a8a7', mState=CANCELLED, mOutputData=Data {}, mTags=[myWorker, com.example.test.MyWorker], mProgress=Data {}}, WorkInfo{mId='a6f46271-e7f7-4f92-87bb-fa2d6e8b8ac2', mState=CANCELLED, mOutputData=Data {}, mTags=[myWorker, com.example.test.MyWorker], mProgress=Data {}}, WorkInfo{mId='d831ee89-d58f-4e7d-8994-4d0fc02370ea', mState=CANCELLED, mOutputData=Data {}, mTags=[myWorker, com.example.test.MyWorker], mProgress=Data {}}, WorkInfo{mId='dd3dca01-d868-465b-8efc-31256b06cc6b', mState=SUCCEEDED, mOutputData=Data {}, mTags=[myWorker, com.example.test.MyWorker], mProgress=Data {}}]
2024-01-18 10:35:26.131 22739-22739 MainActivity            com.example.test                     D  testWorkManager: androidx.work.impl.utils.futures.SettableFuture@ac1927[status=PENDING]
2024-01-18 10:35:26.133 22739-22739 MainActivity            com.example.test                     D  testWorkManager: IN_PROGRESS
2024-01-18 10:35:26.160 22739-22739 MainActivity            com.example.test                     D  testWorkManager: SUCCESS
2024-01-18 10:35:47.721 22739-23012 MyWorker                com.example.test                     D  doWork: 我正在做一些工作
2024-01-18 10:35:47.727 22739-22785 WM-WorkerWrapper        com.example.test                     I  Worker result SUCCESS for Work [ id=b0ba1ea9-8028-45ed-80a9-bb404a6f225c, tags={ com.example.test.MyWorker } ]

这是因为,已经在队列中等待执行的任务是不会被取消的,包括重试回退的任务;这里的cancel是周期性任务使用的,在下一次worker进入队列时生效

其他取消方法

请注意,所有取消方法都是尽力而为的

除了cancelAllWorkByTag,还有下面几个取消方法:

  • cancelWorkById 通过id取消

  • cancelUniqueWork 取消工作链中的工作,这个工作链我们后面进阶讲

  • cancelAllWork 取消所有工作

传参和返回参数

在使用WorkManager中,我们有一些场景需要在使用是传参,在return中返回参数,这时我们就需要使用到setInputData方法,接受一个workDataOf方法

在这里插入图片描述
示例代码如下:

        val mWorkerRequest8= OneTimeWorkRequestBuilder<MyWorker>().addTag("myWorker").setInputData(workDataOf( "MY_DATA" to "这是我传的参数")).build()//通过WorkManager提交WorkRequest,执行MyWorkerWorkManager.getInstance(this).enqueue(mWorkerRequest8)WorkManager.getInstance(this).getWorkInfoByIdLiveData(mWorkerRequest8.id).observe(this) {Log.d(TAG, "testWorkManager: ${it.outputData.getString("MY_DATA")}")}

方法解析
.setInputData(workDataOf( “MY_DATA” to “这是我传的参数”))`:

这行代码设置了工作请求的输入数据。输入数据可以用来传递参数给 Worker 任务。这里,它设置了一个键为 "MY_DATA" 的数据,值为 "这是我传的参数"。这意味着在 MyWorkerdoWork() 方法中,可以通过 inputData 获取这个参数。

MyWorker更改为如下:

import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParametersclass MyWorker(appContext: Context, workerParams: WorkerParameters):Worker(appContext, workerParams) {companion object{private const val TAG = "MyWorker"}override fun doWork(): Result {val input =inputData.getString("MY_DATA") ?: return Result.failure()Log.d(TAG, "doWork: 我正在做一些工作 $input")return Result.success(inputData)}}

这里的inputData就是setInputData中传过来的,然后在success中传递给getWorkInfoByIdLiveData的观察者。

执行结果如下:

2024-01-18 11:35:59.761 12706-12772 MyWorker                com.example.test                     D  doWork: 我正在做一些工作 这是我传的参数
2024-01-18 11:35:59.762 12706-12706 MainActivity            com.example.test                     D  testWorkManager: null
2024-01-18 11:35:59.762 12706-12706 om.example.tes          com.example.test                     I  SmartGc CheckAndAddTask : enable = 1 periodCheck = 1
2024-01-18 11:35:59.781 12706-12750 WM-WorkerWrapper        com.example.test                     I  Worker result SUCCESS for Work [ id=77bfbabb-4234-4db0-8c74-f6d7aeda1b72, tags={ com.example.test.MyWorker, myWorker } ]
2024-01-18 11:35:59.818 12706-12706 MainActivity            com.example.test                     D  testWorkManager: null
2024-01-18 11:35:59.830 12706-12706 MainActivity            com.example.test                     D  testWorkManager: 这是我传的参数

总结

本文主要介绍了WorkManager的一些基础使用,基础基本上学完了,后面学进阶一点的未完期待…

参考资料

WorkManager API
使用 WorkManager 调度任务
官方GitHub 代码示例
WorkManager 使用入门

这篇关于Android WorkManager入门(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/623612

相关文章

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手

Java List 使用举例(从入门到精通)

《JavaList使用举例(从入门到精通)》本文系统讲解JavaList,涵盖基础概念、核心特性、常用实现(如ArrayList、LinkedList)及性能对比,介绍创建、操作、遍历方法,结合实... 目录一、List 基础概念1.1 什么是 List?1.2 List 的核心特性1.3 List 家族成

c++日志库log4cplus快速入门小结

《c++日志库log4cplus快速入门小结》文章浏览阅读1.1w次,点赞9次,收藏44次。本文介绍Log4cplus,一种适用于C++的线程安全日志记录API,提供灵活的日志管理和配置控制。文章涵盖... 目录简介日志等级配置文件使用关于初始化使用示例总结参考资料简介log4j 用于Java,log4c

史上最全MybatisPlus从入门到精通

《史上最全MybatisPlus从入门到精通》MyBatis-Plus是MyBatis增强工具,简化开发并提升效率,支持自动映射表名/字段与实体类,提供条件构造器、多种查询方式(等值/范围/模糊/分页... 目录1.简介2.基础篇2.1.通用mapper接口操作2.2.通用service接口操作3.进阶篇3

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

Python自定义异常的全面指南(入门到实践)

《Python自定义异常的全面指南(入门到实践)》想象你正在开发一个银行系统,用户转账时余额不足,如果直接抛出ValueError,调用方很难区分是金额格式错误还是余额不足,这正是Python自定义异... 目录引言:为什么需要自定义异常一、异常基础:先搞懂python的异常体系1.1 异常是什么?1.2

Python实现Word转PDF全攻略(从入门到实战)

《Python实现Word转PDF全攻略(从入门到实战)》在数字化办公场景中,Word文档的跨平台兼容性始终是个难题,而PDF格式凭借所见即所得的特性,已成为文档分发和归档的标准格式,下面小编就来和大... 目录一、为什么需要python处理Word转PDF?二、主流转换方案对比三、五套实战方案详解方案1:

Spring WebClient从入门到精通

《SpringWebClient从入门到精通》本文详解SpringWebClient非阻塞响应式特性及优势,涵盖核心API、实战应用与性能优化,对比RestTemplate,为微服务通信提供高效解决... 目录一、WebClient 概述1.1 为什么选择 WebClient?1.2 WebClient 与

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

Spring Boot 与微服务入门实战详细总结

《SpringBoot与微服务入门实战详细总结》本文讲解SpringBoot框架的核心特性如快速构建、自动配置、零XML与微服务架构的定义、演进及优缺点,涵盖开发环境准备和HelloWorld实战... 目录一、Spring Boot 核心概述二、微服务架构详解1. 微服务的定义与演进2. 微服务的优缺点三