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.WorkerParameters

class 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,执行MyWorker
        WorkManager
            .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,执行MyWorker
        WorkManager
            .getInstance(this)
            .enqueue(mWorkerRequest4)

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

定义执行范围

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

  //每小时的最后 15 分钟内运行的定期工作
        val mWorkerRequest5 =
            PeriodicWorkRequestBuilder<MyWorker>(
            1, TimeUnit.HOURS, // 执行周期
            15, TimeUnit.MINUTES) // 实际执行时间
            .build()


        //通过WorkManager提交WorkRequest,执行MyWorker
        WorkManager
            .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.WorkerParameters

class 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 = true
        if (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,执行MyWorker
        WorkManager
            .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,执行MyWorker
        WorkManager
            .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,执行MyWorker
        WorkManager
            .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.WorkerParameters

class 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 使用入门

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/332391.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【图解数据结构】深度解析时间复杂度与空间复杂度的典型问题

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;图解数据结构、算法模板 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. ⛳️上期回顾二. ⛳️常见时间复杂度计算举例1️⃣实例一2️⃣实例二3️⃣实例三4️⃣实例四5…

基于R语言的NDVI的Sen-MK趋势检验

本实验拟分析艾比湖地区2010年至2020年间的NDVI数据&#xff0c;数据从MODIS遥感影像中提取的NDVI值&#xff0c;在GEE遥感云平台上将影像数据下载下来。代码如下&#xff1a; import ee import geemap geemap.set_proxy(port7890)# 设置全局网络代理 Map geemap.Map()# 指定…

HCIP-7

IPV6: 为什么使用IPV6&#xff1a; V4地址数量不够V4使用NAT&#xff0c;破坏了端到端原则 IPV6的优点&#xff1a; 全球单播地址聚合性强&#xff08;IANA组织进行合理的分配&#xff09;多宿主----一个接口可以配置N个地址--且这些地址为同一级别自动配置---1&#xff09;…

绝地求生【违规处罚工作公示】1月8日-1月14日

1月8日至1月14日期间&#xff0c;共计对174,636个违规账号进行了封禁&#xff0c;其中164,757个账号因使用外挂被永久封禁。 若您游戏中遇到违规行为&#xff0c;建议您优先在游戏内进行举报&#xff1b; 另外您也可以在官方微信公众号【PUBG国际版】中点击“ 服务中心 - 举报…

Visual Studio 与 SQL Server 常见报错解决方案(工作向)

前言 这篇文章从今天创建开始&#xff0c;会一直更新下去&#xff0c;以后遇到常见但是比较容易解决的报错会在本文进行更新&#xff0c;有需要的朋友可以收藏再看 目录 Visual Studio lc.exe已退出&#xff0c;代码为-1无法导入以下密钥文件xxx.pfx&#xff0c;该密钥文件…

SG-9101CGA(汽车+125°C可编程晶体振荡器)

SG-9101CGA是用于汽车CMOS输出的可编程晶体振荡器&#xff0c;彩用2.5 x 2.0 (mm)封装&#xff0c;0.67 MHz至170 MHz频率范围、工作温度范围为-40℃~125℃&#xff0c;符合车规级晶振&#xff0c;无铅&#xff0c;绿色环保&#xff0c;满足汽车工业标准&#xff0c;电源电压范…

【音视频原理】图像相关概念 ② ( 帧率 | 常见帧率标准 | 码率 | 码率单位 )

文章目录 一、帧率1、帧率简介2、常见帧率标准3、帧率 刷新率 二、码率1、码率简介2、码率单位 一、帧率 1、帧率简介 帧率 Frame Rate , 帧 指的是 是 画面帧 , 帧率 是 画面帧 的 速率 ; 帧率 的 单位是 FPS , Frames Per Second , 是 每秒钟 的 画面帧 个数 ; 帧率 是 动画…

文件共享服务(一)——DAS、NAS、SAN存储类型

一、存储类型 存储类型主要有三种 1. DAS直连式存储 通常由数据线直连电脑就可以用&#xff0c;比如一块新硬盘&#xff0c;只需要利用磁盘模拟器分区&#xff0c;创建文件系统&#xff0c;挂载就可以使用了。 PC中的硬盘或只有一个外部SCSI接口的JBOD存储设备&#xff08;即…

Intel杀回车载计算领域,极氪首发其第一代AI SoC

作者 |德新 编辑 |王博 Intel低调地重新杀回车载计算领域。 在两个月前&#xff0c;在上海举办的进博会上&#xff0c;Intel对外展示了基于新一代酷睿核心打造的智能座舱平台。 在此之前&#xff0c;这家芯片巨头任命了服役公司20多年的老将Jack Weast作为汽车业务的全球负责…

Redis三种缓存读写策略

1. Cache Aside Pattern 旁路缓存模式 1.1 读 1.2 写 1.3 为什么要先更新db再删除cache? 缓存的写入速度是比数据库的写入速度快很多,因此相比于先删除cache后更新db带来数据不一致性问题的概率更小。 1.4 特点 平时使用比较多的一个缓存读写模式同时维系db 和 cache&#…

C#:接口中如何将某个值类型的字段传null?

在实际对接第三方接口时&#xff0c;偶尔会有一些字段在某些情况下是不需要传值的。那如何处理呢&#xff1f; 有两种方法&#xff1a; 1、将值类型改为可空类型&#xff1b; 2、定义基类&#xff0c;基类包含所有必须要传的字段&#xff0c;子类则加入偶尔需要传的字段。 下…

联合体中嵌套结构体,结构体未命名时,结构体成员变量的引用

参考文章&#xff1a;C语言 结构体 联合体 | 嵌套使用_联合体里面嵌套结构体-CSDN博客 如题&#xff0c;其实直接用 联合体名.结构体成员变量名 即可。 程序&#xff1a; #include <stdio.h>typedef unsigned int uint32_t; typedef unsigned char uint8_t;union b…

Spring-BeanPostProcessor PostConstruct init InitializingBean 执行顺序

执行顺序探究 新建一个对象用于测试 Component public class Student implements InitializingBean {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}pu…

小程序进阶学习(视频完结)(核心,重点)

首先上面是一个视频播放器 把视频的宽度设置为100%即可铺满全屏 然后视频的标题和作者 最后就是一个视频播放列表 &#xff0c;设置一个固定位置开始滚动即可 还有一个问题没有解决&#xff0c;大家出出主意。 在播放页面在点击一个新的视频去播放&#xff0c;点进去的新视频获…

基于yolov2深度学习网络的车辆检测算法matlab仿真,包括白天场景和夜晚场景

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 YOLOv2算法原理 4.2 车辆检测原理 4.3 白天场景和夜晚场景的车辆检测 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 load yolov2.mat%…

前端发展趋势:WebAssembly、PWA 和响应式设计

目录 前言 WebAssembly&#xff1a;超越JavaScript的性能 渐进式Web应用&#xff08;PWA&#xff09;&#xff1a;离线可用和更好的用户体验 响应式设计&#xff1a;适应多种设备 总结 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊前端…

Android现代开发推荐 | Android Showcase 2.0

Android现代开发推荐 | Android Showcase 2.0 Android Showcase是一个完整的Android应用程序示例&#xff0c;它使用了现代的Android应用程序开发方法&#xff0c;集成了流行的开发工具、库和代码检查工具&#xff0c;以及强大的测试框架和持续集成&#xff08;CI&#xff09;…

python数字图像处理基础(八)——harris角点检测、图像尺度空间、SIFT算法

目录 harris角点检测原理函数 图像尺度空间概念局部不变性局部不变特征SIFT算法 harris角点检测 原理 Harris 角点检测是一种用于在图像中检测角点的算法。角点是图像中局部区域的交叉点或者突出的特征点。Harris 角点检测算法旨在寻找图像中对于平移、旋转和尺度变化具有不变…

数据结构:链式栈

stack.h /* * 文件名称&#xff1a;stack.h * 创 建 者&#xff1a;cxy * 创建日期&#xff1a;2024年01月18日 * 描 述&#xff1a; */ #ifndef _STACK_H #define _STACK_H#include <stdio.h> #include <stdlib.h>typedef struct stack{int data…

FPGA物理引脚,原理(Pacakge and pinout)-认知3

画FPGA芯片引脚封装图&#xff08;原理&#xff09;&#xff0c;第一是参考开发板(根据一下描述了解总览&#xff09;&#xff0c;第二是研究Datasheet. ASCII Pinout File Zynq-7000 All Programmable SoC Packaging and Pinout(UG585) 1. Pacakge overview 1.1&#xff0…