IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android WorkManager -> 正文阅读

[移动开发]Android WorkManager

1.WorkManager

在Google发布的Android Jetpack架构中,有一个专门用于安排和管理后台任务的库WorkManager 。WorkManager会考虑到操作系统电池优化功能(如Doze,待机等)的限制,在任何情况下(包括启动它的应用已经退出,甚至设备重启)仍然承诺保证执行工作,并且它有自己的数据库来维护任务。此外,很容易计划、取消和管理多个工作顺序和平行的执行。

下图是一张其总的架构图:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5a2f6Iqz6Iqz,size_20,color_FFFFFF,t_70,g_se,x_16

整个WorkManager的执行流程如下图所示:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5a2f6Iqz6Iqz,size_20,color_FFFFFF,t_70,g_se,x_16

① 给WorkManager发送工作请求WorkRequest。

② WorkManager将该请求的相关参数放入WorkManager的数据库中。

③ WorkManager根据设备版本、是否是前台任务等情况将请求操作传递给JobScheduler或者AlarmManager等部件。

④ 检查Worker是否满足约束条件,当满足约束条件时调用执行Worker。

?

2.WorkManager的核心类

(1)Worker

Worker是一个抽象类,这个类用来指定具体需要执行的任务。使用时要继承这个类并且实现里面的doWork()方法,在其中写具体的业务逻辑。

(2)WorkRequest

代表一项任务请求。一个WorkRequest对象至少要指定一个Worker类。同时,还可以向WorkRequest对象添加、指定任务应运行的环境等。每个WorkRequest都有一个自动生成的唯一ID, 可以使用该ID来执行诸如取消排队的任务或获取任务状态等操作。

WorkRequest是一个抽象类,有两个直接子类 OneTimeWorkRequest和 PeriodicWorkRequest。与WorkerRequest相关的有如下两个类:

① WorkRequest.Builder:用于创建WorkRequest对象的助手类 ,它有两个子类OneTimeWorkRequest.Builder和 PeriodicWorkRequest.Builder,分别对应两者创建上述两种WorkerRequest。

② Constraints:指定任务运行时的限制(例如,“仅在连接到网络时才能运行”)。可以通过 Constraints.Builder来创建该对象,并在调用WorkRequest.Builder的build()方法之前,将其传递给WorkerRequest。

(3)WorkManager

用来安排和管理工作请求。创建的WorkRequest对象就是通过WorkManager来安排顺序的。

WorkManager调度任务的时候会分散系统资源,做好类似负载均衡的操作,同时会遵循前面设置的对任务的约束条件。

(4)WorkStatus

每一个WorkRequest都会有一个WorkStatus与之对应,里面包含了该任务的许多信息,可以通过WorkManager来获取包含WorkStatus的LiveData对象。开发者可以通过观察该LiveData对象来监听与之对应的任务所处的状态,并在任务完成后通过调用WorkStatus的getOutputData()方法获取返回值。

?

3.典型的使用

①创建Worker

首先,创建自己的Worker类,并重写它的doWork()方法,并根据情况返回执行结果状态。

public class MyWorker extends Worker {

? ? @Override

? ? public Worker.Result doWork() {

? ? ? ? doSomething(); // 执行业务逻辑

? ? ? ? // 根据执行情况进行返回,返回SUCCESS代表任务执行成功;返回FAILURE代表任务执行失败,并且此时不会再重新执行任务;返回RETRY,WorkManager会在之后再次尝试执行任务。

? ? ? ? return Result.SUCCESS;

? ? }

}

②创建Constraints和WorkerRequest

如果有必要,可以指定任务运行时的限制(例如,想要指定该任务只应在设备闲置并接通电源时运行)。然后根据前面创建的Worker来创建WorkerRequest,并将任务约束Constraints 传递给它:

// 创建一个WorkerRequest的任务约束

Constraints constraints = new Constraints.Builder()

? ? .setRequiresDeviceIdle(true)

? ? .setRequiresCharging(true)

? ? ……// 还有许多其他任务约束可以添加

? ? .build();

// 创建一个OneTimeWorkRequest 并将上面的任务约束传给它

OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(CompressWorker.class)

? ? .setConstraints(constraints)

? ? .build();

③WorkManager运行任务并监听结果

获取WorkManager并选择合适的时间来运行任务。如果需要检查任务状态,就可以通过WorkManager来获取WorkerRequest的WorkStatus句柄来获取WorkStatus对象,同时可以对该对象进行监听。

WorkManager.getInstance().enqueue(workRequest);

//通过WorkerRequest的id来获取LiveData

WorkManager.getInstance().getStatusById(workRequest .getId())

? ? .observe(lifecycleOwner, workStatus -> {

? ? ? ? //当workStatus状态改变时候可以根据业务需要进行操作

? ? ? ? if (workStatus != null && workStatus.getState().isFinished()) {

? ? ? ? ? ? // 如果任务执行完毕,可以在这里进行操作

? ? ? ? }

? ? });

④取消任务

WorkerRequest排入队列后,可以取消任务。可以通过id或者tag进行对应任务的取消。WorkManager会尽最大努力取消任务,但这本质上是不确定的,有可能在尝试取消任务时,任务已经在运行了或者已经运行完成了。

UUID compressionWorkId = compressionWork.getId();

WorkManager.getInstance().cancelWorkById(compressionWorkId);

⑤其他操作

1.1 重复执行任务

创建WorkRequest的时候用OneTimeWorkRequest ,代表其只执行一次。WorkRequest还有另外一个子类 PeriodicWorkRequest,可以用它来定时循环执行任务。

要创建循环任务,可以使用 PeriodicWorkRequest.Builder,该类创建一个 PeriodicWorkRequest对象,然后PeriodicWorkRequest按照与OneTimeWorkRequest对象相同的方式入队 。

// 第二个参数是间隔时间,第三个参数是第二个参数的单位

PeriodicWorkRequest.Builder myPeriodicBuilder =?new PeriodicWorkRequest.Builder( MyWorker.class, 12, TimeUnit.HOURS);

// ...如果有必要,在这里可以给builder加上约束..

PeriodicWorkRequest myPeriodicWork = myPeriodicBuilder .build();

WorkManager.getInstance().enqueue( myPeriodicWork );

1.2 链接任务

有时候可能需要按特定顺序运行多个任务。 WorkManager允许开发者创建和排队指定多个任务的工作序列,以及设置他们相应的运行顺序。

例如,假设有三个OneTimeWorkRequest对象:workA、workB和 workC。这些任务必须按照A、B、C 顺序依次运行。要入队它们,用WorkManager.beginWith() 方法创建一个序列 ,传递第一个OneTimeWorkRequest对象; 该方法会返回一个WorkContinuation对象,可以通过它来依次按顺序添加剩余的OneTimeWorkRequest,最后将整个序列排入WorkContinuation.enqueue():

WorkManager.getInstance()

? ? // 首先运行A类任务

? ? .beginWith(workA1, workA2, workA3)

? ? // 当所有A类任务运行完毕再运行B类任务

? ? .then(workB)

? ? // 接着再运行C类任务

? ? .then(workC1, workC2)

? ? .enqueue();

也可以通过使用WorkContinuation.combine() 方法连接多个链来创建更复杂的序列 。例如,假设要运行如下序列:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5a2f6Iqz6Iqz,size_14,color_FFFFFF,t_70,g_se,x_16

?建立这个序列,创建两个单独的链,然后将它们连接在一起成为第三个链:

WorkContinuation chain1 = WorkManager.getInstance()

? ? .beginWith(workA)

? ? .then(workB);

WorkContinuation chain2 = WorkManager.getInstance()

? ? .beginWith(workC)

? ? .then(workD);

WorkContinuation chain3 = WorkContinuation

? ? .combine(chain1, chain2)

? ? .then(workE);

chain3.enqueue();

虽然WorkManager每个子链的运行有序,但是chain1 和 chain2之间的运行顺序就无法保证了。例如,workB可能在workC之前或之后运行,或者它们可能同时运行。能保证的是每个子链中的任务将按顺序运行; 也就是说,workB直到workA 完成后才开始。combine()方法还能这么用WorkContinuation.combine(OneTimeWorkRequest, WorkContinuation…)也就是链和单个WorkRequest的结合。

1.3 唯一工作序列

可以创建一个唯一的工作序列,通过调用函数 beginUniqueWork() 开始而不是beginWith()。每个唯一的工作序列都有一个名字;WorkManager只允许一个具有该名称的工作序列存在。当创建一个新的唯一工作序列时,WorkManager如果已经有一个待处理的序列具有相同的名字会根据传入的策略标志不同有如下三种操作:

① KEEP:保留现有序列并忽略新来的序列

② REPLACE:取消现有的序列并将其替换为新序列

③ APPEND:将新序列附加到现有序列,在现有序列的最后一个任务完成后运行新序列的第一个任务。

如果任务不应多次排队,就可以使用唯一工作序列。例如,如果想把数据同步到网络上,可以入队一个名为“同步”的序列,并将策略标志传入KEEP,这样在此期间新的同步工作请求就都会被忽略了。

1.4 设置标签

可以为任何WorkRequest对象分配标记字符串来对任务进行分组 。要设置标签,可调用WorkRequest.Builder.addTag()方法,例如:

OneTimeWorkRequest myWorkRequest = new OneTimeWorkRequest.Builder(MyWorker.class)

? ? .setConstraints(myConstraints)

? ? .addTag("myWork")

? ? .build();

WorkManager类提供了几种实用方法,可以使用特定标签对所有任务进行操作。例如 WorkManager.cancelAllWorkByTag() 取消具有特定标记的所有任务,WorkManager.getStatusesByTag() 返回具有该标记的所有任务的WorkStatus。

1.5 输入和输出

为了获得更大的灵活性,可以将参数传递给任务,并让任务返回结果。传递和返回的值是键值对。要将参数传递给任务,在创建WorkRequest 对象之前调用WorkRequest.Builder.setInputData() 。该方法传入Data对象,其通过Data.Builder进行创建。Worker类可以通过调用Worker.getInputData()来访问这些参数 。要输出返回值,任务调用 Worker.setOutputData(),该方法的参数也是一个Data对象; 可以通过观察任务的LiveData<WorkStatus>获得该输出。这边还要说一下对于传入的参数目前只能是一些int boolean String的基本数据和对应的数组结构,而且总大小要小于10k,不能传入对象。

下面是定义一个Worker 类的例子:

// 定义 Worker 类:

public class MathWorker extends Worker {

? ? // 定义传入参数的key:

? ? public static final String KEY_X_ARG = "X";

? ? public static final String KEY_Y_ARG = "Y";

? ? public static final String KEY_Z_ARG = "Z";

? ? // ...定义输出参数的key:

? ? public static final String KEY_RESULT = "result";

? ? @Override

? ? public Worker.Result doWork() {

? ? ? ? // 抓取传入参数和如果设置抓取失败的默认值

? ? ? ? int x = getInputData().getInt(KEY_X_ARG, 0);

? ? ? ? int y = getInputData().getInt(KEY_Y_ARG, 0);

? ? ? ? int z = getInputData().getInt(KEY_Z_ARG, 0);

? ? ? ? // ...do something..

? ? ? ? int result = ...

? ? ? ? //...设置输出参数

? ? ? ? Data output = new Data.Builder()

? ? ? ? ? ? .putInt(KEY_RESULT, result)

? ? ? ? ? ? .build();

? ? ? ? setOutputData(output);

? ? ? ? return Result.SUCCESS;

? ? }

}

入队上述的worker类并传入参数:

// 创建一个Data 对象:

Data myData = new Data.Builder()

? ? // 传入输入值

? ? .putInt(KEY_X_ARG, 42)

? ? .putInt(KEY_Y_ARG, 421)

? ? .putInt(KEY_Z_ARG, 8675309)

? ? // ...调用build()进行创建:

? ? .build();

// ..创建并入队一个任务并给这个任务设置一个输入参数

OneTimeWorkRequest mathWork = new OneTimeWorkRequest.Builder(MathWorker.class)

? ? ? ? .setInputData(myData)

? ? ? ? .build();

WorkManager.getInstance().enqueue(mathWork);

输出值会存在对应WorkRequest的WorkStatus中:

WorkManager.getInstance().getStatusById(mathWork.getId())

? ? .observe(lifecycleOwner, status -> {

? ? ? ? if (status != null && status.getState().isFinished()) {

? ? ? ? ? int myResult = status.getOutputData().getInt(KEY_RESULT,

? ? ? ? ? ? ? ? ? myDefaultValue));

? ? ? ? ? // ...根据返回值做相应处理 ...

? ? ? ? }

? ? });

如果是一个链接任务,则前一个任务的输出可用作链中下一个任务的输入。如果是一个简单的单任务链,前一个任务通过调用setOutputData()返回结果 ,后一个任务通过调用getInputData()来获取结果 。如果链更复杂, 例如,因为几个任务都将输出发送到单个后续任务,就需要通过InputMerger来处理了。

1.6 合并输入参数

前面已经说到了,前一个任务的输出可用作链中下一个任务的输入。但是当遇到如下情况就有个问题,一个work有多个前置work:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5a2f6Iqz6Iqz,size_17,color_FFFFFF,t_70,g_se,x_16

?如上图所示,当一个任务有两个前置任务时,这是后直接使用前面输出的output Data,就要给它设置InputMerger策略。来对work1 的output 和work2 的output 进行合并。合并策略现在有如下两种OverwritingInputMerger和ArrayCreatingInputMerger。

①OverwritingInputMerger

这个策略是用来将前面的output 进行覆盖合并,如果两个output 有相同的key,则后者会将前者覆盖,有key 就给input新加一组key,value对。

第一组数据传入input:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5a2f6Iqz6Iqz,size_20,color_FFFFFF,t_70,g_se,x_16

?第二组覆盖新增之前的:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5a2f6Iqz6Iqz,size_20,color_FFFFFF,t_70,g_se,x_16

?②ArrayCreatingInputMerger

这个策略是用来将前面的output 进行全部保留合并,如果两个output 有相同的key,则会同时保留两者的数据,有key 就给input新加一组key,value对。如果有相同的key但是是不同的数据类型,则会抛出异常。

合并两组数据:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5a2f6Iqz6Iqz,size_20,color_FFFFFF,t_70,g_se,x_16

?使用时候给接收数据的WorkRequest传入对应的策略就好了:

OneTimeWorkRequest mathWork = new OneTimeWorkRequest.Builder( MathWorker.class)

? ? ?.setInputMerger( OverwritingInputMerger.class)

? ? ? ? .build();

?

?

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 18:36:39  更:2022-03-30 18:39:24 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/17 14:48:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码