Android WorkManager使用介绍

news2024/11/15 7:10:10

一、引言

  WorkManager 是google提供的异步执行任务的管理框架,是 Android Jetpack 的一部分,会根据手机的API版本和应用程序的状态来选择适当的方式执行任务。
  在后台执行任务的需求是非常常见的,Android也提供了多种解决方案,如JobScheduler、Loader、Service等,如果这些API没有被恰当使用,则可能会消耗大量电量。Android在解决应用程序耗电问题上做了各种尝试,从Doze到App Standby,通过各种方式限制和管理应用程序,以保证应用程序不会在后台消耗过多的设备电量。WorkManager为应用程序中那些不需要及时完成的任务提供了一个统一的解决方案,以便在设备电量和用户体验之间达到一个比较好的平衡

二、WorkManager特点

  • 针对的是不需要及时完成的任务
    例如,发送应用程序日志、同步应用程序数据、备份用户数据等,这些任务一般都不需要立即完成,如果我们自己来管理这些任务,逻辑可能会非常复杂,若API使用不恰当,可能会消耗大量电量。
  • 保证任务一定会执行
    WorkManager能保证任务一定会被执行,即使应用程序当前不在运行中,甚至在设备重启过后任务仍然会在适当的时刻被执行。WorkManager有自己的数据库,关于任务的所有信息和数据都保存在该数据库中。因此只要任务交给了WorkManager,哪怕应用程序彻底退出或者设备被重新启动,WorkManager依然能够保证完成任务。
  • 兼容范围广
    WorkManager最低能兼容API Level 14,并且不需要设备安装Google Play Services。因此,不用过于担心兼容性问题,因为API Level 14已经能够兼容几乎100%的设备了。

三、WorkManager兼容方案

  WorkManager能根据设备的情况,选择不同的执行方案。在API Level 23以上的设备中通过JobScheduler完成任务,在API Level 23以下的设备中,通过AlarmManager和Broadcast Receivers组合来完成任务。但无论采用哪种方案,任务最终都是由Executor来执行的。另外,WorkManager不是一种新的工作线程,它的出现不是为了替代其他类型的工作线程。工作线程通常立即运行,并在任务执行完成后给用户反馈,而WorkManager不是即时的,它不能保证任务能立即得到执行
兼容方案

四、WorkManager使用方法

1. 使用前的准备

导入依赖

将依赖项添加到应用的 build.gradle 文件中:

dependencies {
    def work_version = "2.7.1"
    implementation "androidx.work:work-runtime:$work_version"
}

创建Activity

新建一个Activity,在其布局文件中放置一个按钮,作为任务的触发器。

2. 定义任务

新建一个MyWork类继承Worker类,重写dowork()方法,在其中添加希望由WorkManager 运行的工作任务。

import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
 
public class MyWork extends Worker {
 
    public MyWork(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }
 
    @NonNull
    @Override
    public Result doWork() {
        //任务的具体行为
        Log.d("tag", "Work doWork");
        return Result.success();
    }
}

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

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

3. 配置任务

任务通过WorkRequest在WorkManager中进行定义,为了使用WorkManager调度任务,需要先创建一个WorkRequest对象,将其加入队列。

//为任务创建WorkRequest对象
WorkRequest myWorkRequest = ...
//将任务请求加入队列
WorkManager workManager = WorkManager.getInstance(this);
workManager.enqueue(myWorkRequest);

其中,WorkRequest对象有两种类型:

  • OneTimeWorkRequest:只会执行一次的任务请求
  • PeriodicWorkRequest:将以周期形式反复执行的任务请求

WorkRequest对象包含WorkManager调度和运行工作所需的所有信息。其中包括运行工作必须满足的约束、调度信息(例如延迟或重复间隔)、重试配置,并且可能包含输入数据(如果工作需要),下面以一次性任务请求为例,介绍各种配置信息的定义方式与实际效果

设置任务触发条件

需要实例化一个Constraints对象指定任务运行的约束(触发条件),常用的约束类型如下:

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

以NetworkType为例,当将网络的约束设置为无要求时,任务将立刻执行,但当对网络类型有要求时,即使已经处在符合要求的网络环境下,任务也不是立刻执行,而是由系统选择合适的时间再执行。

 public void myAddWork(View view) {
        //定义触发条件
        Constraints constraints = new Constraints.Builder()
                //NetworkType.NOT REQUIRED: 对网络没有要求
                //NetworkType.CONNECTED: 网络连接的时候执行
                //NetworkType.UNMETERED:环计费的网络比如WIFI下执行
                //NetworkType.NOT ROAMING:非漫游网络状态执行
                //NetworkType.METERED: 计费网络比如3G,4G下执行
                .setRequiredNetworkType(NetworkType.NOT_REQUIRED)
                .build();
        OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWork.class)
                //设置触发条件
                .setConstraints(constraints)
                .build();
}

执行效果如图:
无约束任务

设置延迟执行任务与任务标签

当队列中的任务满足了全部约束条件后,系统可能会立即运行该任务。可以通过设置延迟时间来使任务经过一段时间后再启动,下面举例说明了如何将任务设置为在加入队列后至少经过5秒后再运行。

OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWork.class)
        //设置延迟执行
        .setInitialDelay(5, TimeUnit.SECONDS)
        .build();

运行效果如图:
延迟任务
在WorkRequest中可以为单个任务设置标签,也可以为一组具有逻辑联系的多个任务设置相同的标签,标签可以在任务状态操作中作为任务的标识,可以通过标签来监听任务状态,也可以取消所有具有相同标签的任务。

OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWork.class)
        //设置tag标签
        .addTag("myWorkRequest")
        .build();

设置重试和退避策略

当任务的返回值为Result.retry()时,系统需要根据一定的策略来决定每次重试的间隔时间,策略中包含退避延迟时间退避策略。其中,退避延迟时间指定了首次尝试后重试工作前的最短等待时间,其值不得小于10秒;退避策略定义了退避延迟时间随时间以怎样的方式增长,取值分为两种,线性倍数增长:LINEAR 和指数型增长:EXPONENTIAL。
系统默认的策略是EXPONENTIAL,延迟时间为 10 秒,我们也可以在WorkRequest中自定义策略,下面举例设置了一个线性增长的,等待时间初值为12秒的策略。

OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWork.class)
        //设置退避策略
        .setBackoffCriteria(BackoffPolicy.LINEAR, Duration.ofSeconds(12))
        .build();

4. 观察任务状态与取消任务

对于只执行一次的任务请求,其生命周期的状态转换如图所示:
一次性任务
对于周期性的任务请求,不存在SUCCESSED和FAILED状态,因为周期性任务永远不会结束,每次运行后,无论结果如何,系统都会重新对其进行调度,周期性任务唯一的终止状态为CANCELLED,其生命周期的状态转换如图所示:
周期性任务
在任务运行的时候,我们可以随时通过任务id或任务标签来查询其状态,利用对应的LiveData方法可以注册监听器来观察WorkInfo的变化。

//观察任务状态
workManager.getWorkInfoByIdLiveData(workRequest.getId()).observe(this, new Observer<WorkInfo>() {
    @Override
    public void onChanged(WorkInfo workInfo) {
        Log.d("workInfo", "onChanged: "+workInfo.toString());
    }
});

任务状态
当不再需要运行先前加入队列的任务时,可以根据任务id或任务标签来取消任务,WorkManager 会在后台检查工作的状态。如果工作已经完成,系统不会执行任何操作。否则,工作的状态会更改为CANCELLED,之后就不会运行这个工作。任何依赖于此工作的WorkRequest的状态也将变为CANCELLED。下面举例了在任务运行前将其取消的情况:

OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWork.class)
           //设置延迟5秒执行
          .setInitialDelay(5, TimeUnit.SECONDS)
          .build();
 
//将任务提交给WorkManager
workManager.enqueue(workRequest);
 
//根据任务id取消任务
new Timer().schedule(new TimerTask() {
    @Override
    public void run() {
        workManager.cancelWorkById(workRequest.getId());
    }
}, 2000);

取消任务

5. 参数传递

WorkManager可以与具体任务之间互相传递参数,在定义WorkRequest对象时可以将参数传入任务,所传参数是一个Data类型的实例化对象。


//定义所要传递的数据
Data input = new Data.Builder()
        .putString("inputData", "输入信息")
        .build();
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWork.class)
        //参数传递
        .setInputData(input)
        .build();

在任务中可以获得传入的数据并使用,相应的,也可以将数据从任务中传递回WorkManager。

public Result doWork() {
    Log.d("tag", "Work doWork");
 
    //获取传递至任务中的数据
    String input = getInputData().getString("inputData");
    Log.d("input", input);
    
    //定义数据并通过返回值传回WorkManager
    Data output = new Data.Builder()
            .putString("outputData", "执行成功")
            .build();
    return Result.success(output);
}

在观察任务状态的监听器中增加以下代码,获取任务传递回来的数据并使用。


workManager.getWorkInfoByIdLiveData(workRequest.getId()).observe(this, new Observer<WorkInfo>() {
    @Override
    public void onChanged(WorkInfo workInfo) {
        Log.d("workInfo", workInfo.toString());
        
        //当任务执行状态为SUCCESSED时,获取回传的数据信息
        if (workInfo != null && workInfo.getState() == WorkInfo.State.SUCCEEDED){
            String output = workInfo.getOutputData().getString("outputData");
            Log.d("output", output);
        }
    }
});

运行结果如图:
数据传递

6. 配置周期性任务

上述示例均为一次性任务,在实际开发中可能需要定期运行某些任务。例如,定期备份数据、定期下载应用中的新鲜内容或者定期上传日志到服务器,此时则需要用到WorkRequest的另一种类型:PeriodicWorkRequest,创建对象的代码如下:

PeriodicWorkRequest PeriodicRequest =
       new PeriodicWorkRequest.Builder(myWorker.class, 2, TimeUnit.HOURS)
           // Constraints
           .build();

在此示例中,工任务的运行时间间隔定为2小时,需要注意的是,可以定义的间隔时间最短不得少于15分钟。

7. 任务链与任务组合

当需要以特定顺序运行多个任务时,可以创建任务链并将其加入队列,任务链用于指定多个依存任务并定义这些任务的运行顺序。
新建两个类AWorker和BWorker继承Worker类,将其按照任务链形式添加进任务列表

OneTimeWorkRequest ARequest = new OneTimeWorkRequest.Builder(AWorker.class)
        .build();
OneTimeWorkRequest BRequest = new OneTimeWorkRequest.Builder(BWorker.class)
        .build();
//先运行A,再运行B
WorkManager.getInstance(this)
        .beginWith(ARequest)
        .then(BRequest)
        .enqueue();

运行结果如图:
任务链
有时一些任务需要在若干个任务执行完毕后方可执行,此时就需要用到任务组合。定义五个任务来模拟图中的顺序执行情况
运行顺序

//创建五个任务请求
OneTimeWorkRequest ARequest = new OneTimeWorkRequest.Builder(AWorker.class)
        .build();
OneTimeWorkRequest BRequest = new OneTimeWorkRequest.Builder(BWorker.class)
        .build();
OneTimeWorkRequest CRequest = new OneTimeWorkRequest.Builder(CWorker.class)
        .build();
OneTimeWorkRequest DRequest = new OneTimeWorkRequest.Builder(DWorker.class)
        .build();
OneTimeWorkRequest ERequest = new OneTimeWorkRequest.Builder(EWorker.class)
        .build();
//将同一条任务链上的任务执行顺序定义成一个任务组合
WorkContinuation continuation1 =  WorkManager.getInstance(this)
        .beginWith(ARequest)
        .then(BRequest);
WorkContinuation continuation2 =  WorkManager.getInstance(this)
        .beginWith(CRequest)
        .then(DRequest);
//把两个任务组合放入一个集合
List<WorkContinuation> taskList = new ArrayList<>();
taskList.add(continuation1);
taskList.add(continuation2);
//设置当集合中的所有任务组合都运行完毕后,才运行任务E
WorkContinuation.combine(taskList)
        .then(ERequest)
        .enqueue();

运行结果如图:
任务组合
作者:凌语桐
原文链接:https://blog.csdn.net/pandcu/article/details/128167687?spm=1001.2014.3001.5502

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高阶数据结构:并查集

本篇主要是介绍并查集的内容&#xff1a;所谓并查集就是一种描述不相交集合的数据结构&#xff0c;即若一个问题涉及多个元素&#xff0c;它们可以划分到不同集合&#xff0c;同属一个集合内的元素等价&#xff0c;不同集合内的元素不等价。 文章目录 一、并查集原理二、并查集…

Prophet在R语言中进行时间序列数据预测

您将学习如何使用Prophet&#xff08;在R中&#xff09;解决一个常见问题&#xff1a;预测公司明年的每日订单。 最近我们被客户要求撰写关于时间序列的研究报告&#xff0c;包括一些图形和统计输出。 数据准备与探索 Prophet最适合每日数据以及至少一年的历史数据。 我们将…

Ansible

Ansible是什么&#xff1f; Ansible是一个基于eythn开发的配置管理和应用部署工具&#xff0c;现在也在自动化管理领域大放异彩。它融合了众多老牌运维工具的优点&#xff0c;Pbet和Saltstack能实现的功能&#xff0c;As;ble基本上都可以实现。Ansible能批量配置、部署、管理上…

MongoDB入门与实战-第五章-MongoDB副本集

目录参考一、副本集概念1、**主要功能**2、主从复制和副本集区别3、复制结构图二、副本集成员角色1.主节点2.副本节点3.仲裁节点三、副本集架构&#xff08;一主一副本一仲裁&#xff09;1、**设置读操作权限&#xff1a;**2、取消作为奴隶节点的读权限四、选举原则1、触发条件…

时序逻辑电路

数字电路分为两大类&#xff0c;组合逻辑电路和时序逻辑电路 今天我们要开始学习的是时序逻辑电路&#xff0c;要求大家掌握 同步逻辑电路的分析方法 常用的时序逻辑电路的使用方法 当然由于时序逻辑电路的特点&#xff0c;使得时序逻辑电路难度远远大于组合逻辑电路 电路任…

一文详解Redis键过期策略,最全文档

文章目录1 设置带过期时间的 key1.1 刷新过期时间1.2 Redis 之前的 2.1.3 的差异1.3 返回值1.4 示例1.5 带过期时间的 key1.6 过期精度1.7 过期和持久化2 Redis的key过期策略2.1 惰性删除2.1.1 优点2.1.2 缺点定时删除优点缺点2.2 定期删除优点缺点难点定期删除流程Redis采用的…

SpringMVC异常处理器

1.异常 1.1 异常结构层次 异常主要分为&#xff1a;错误、一般性异常&#xff08;检查异常&#xff09;、运行期异常&#xff08;非受控异常&#xff09; 错误&#xff1a;如果应用程序出现了 Error&#xff0c;那么将无法恢复&#xff0c;只能重新启动应用程序&#xff0c;最…

VUE3-工作笔记-06

本节&#xff1a;总结11月份学习的内容 1.如果有很多个功能是一样的&#xff0c;那就先写一个测试很多次&#xff0c;确定没有bug再全部改&#xff0c;不然辛辛苦苦全部改了之后&#xff0c;发现还是有bug又要重新改&#xff0c;最好是写成组件的形式&#xff0c;写成hook。 …

ACM第三周---周训---题目合集.

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石.CSDN &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​ &#x1f4e3;系列专栏&#xff1a;ACM周训练题目合集.CSDN &#x1f4ac;总结&#xff1a…

微服务框架 SpringCloud微服务架构 10 使用Docker 10.7 数据卷命令

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 SpringCloud微服务架构 文章目录微服务框架SpringCloud微服务架构10 使用Docker10.7 数据卷命令10.7.1 数据卷10.7.2 操作数据卷10.7.3 直接…

度量方法总结

本文目录0 引言1 度量差异性1.1 闵可夫斯基距离&#xff08;Minkowski distance&#xff09;1.1.1 欧氏距离&#xff08;Euclidean distance&#xff09;1.1.2 曼哈顿距离&#xff08;Manhattan distance&#xff09;1.1.3 切比雪夫距离&#xff08;Chebyshev distance&#xf…

[附源码]JAVA毕业设计课程网站设计(系统+LW)

[附源码]JAVA毕业设计课程网站设计&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&…

Spring Boot 框架整合 MyBatis 连接数据库,详细说明

在我们日常的开发过程中&#xff0c;肯定不可避免的会使用到数据库以及 SQL 语句。比如&#xff0c;刚开始学习 Java 的时候可能会遇到 JDBC&#xff0c;它是连接 Java 和数据库的桥梁&#xff0c;我们可以使用 JDBC 来建立与数据库之间的连接并且执行相应的 SQL 语句。虽然 JD…

数据结构—List集合

文章目录一、ArrayList集合1&#xff0c;ArrayList集合的特点2&#xff0c;ArrayList集合的一些方法添加元素个数获取指定位置元素指定位置插入元素替换元素移除元素判断是否为空判断是否包含元素移除指定元素迭代器遍历集合二、LinkedList集合1&#xff0c;LinkedList集合的特…

Ra-01SC/Ra-01SCH模组驱动

目录简介硬件准备设备清单接线说明重要函数说明调试联系我们简介 本文档主要介绍Ra-01SC/Ra-01SCH模组驱动例程使用方法&#xff1b; 硬件准备 设备清单 硬件数量Ra-001SC/Ra-01SCH 模组2个杜邦线若干USB转TTL模块2个STM32F103C8T6开发板2个安卓线2根 接线说明 STM32F103C…

[论文阅读] 颜色迁移-直方图渐进式颜色迁移

[论文阅读] 颜色迁移-直方图渐进式颜色迁移 文章: 有2篇相关的文章, 都为同一作者 [Progressive histogram reshaping for creative color transfer and tone reproduction], [code未公开][Progressive color transfer for images of arbitrary dynamic range], [code未公开]…

qt模块feature QT_FEATURE_* qt_lib_*.pri QT_CONFG qtConfig

qt为方便对代码进行删减&#xff0c;将代码拆分成很多模块&#xff0c;对每个模块梳理出feature&#xff0c;为了简化程序的使用和其他目的&#xff0c;qt还对每个模块分成public模块和private模块&#xff08;比如core分为core和core-privare模块&#xff0c;但两者公用同一个…

迭代器失效问题

目录 一、vector迭代器失效问题 1、resize,reserve,insert,assign,push_back可能引起底层空间改变 2、指定位置元素的删除操作erase 3、Linux下&#xff0c;g编译器对迭代器失效的检测不是非常严格&#xff0c;处理也没有vs下极端 4、与vector类似&#xff0c;string在插入…

java swing(GUI) MySQL实现的飞机票预定系统源码带视频运行教程

大家好&#xff0c;今天给大家演示一下由Java swing实现的飞机票预定系统&#xff0c;系统数据库原本采用的是Oracle&#xff0c;我又改了一个mysql版本的&#xff0c;所以这套系统有两个版本&#xff0c;一个是mysql数据库版的&#xff0c;一个是Oracle数据库版&#xff0c;演…

TypeScript是强类型,静态类型的Java Script

1. 编程语言的分类 As we all know, JavaScript 是弱类型&#xff0c;动态类型的编程语言。 首先我们来解释一下这几个名词&#xff1a; 动态类型语言&#xff1a;在 运行期间(Runtime) 才去做数据类型检查的语言。静态类型语言&#xff1a;在 编译其间(Compile) 就进行数据…