WorkManager架构组件是用来管理后台工做任务。这个时候你可能会奇怪了Android不是已经 有不少管理后台任务的类了么,好比JobScheduler, AlarmManger、在好比AsyncTask, ThreadPool。WorkManager。WorkManager的优点在哪里,咱们为啥要使用WorkManager。咱们从两个方面来讲明WorkManager的优点java
WorkManager对比JobScheduler, AlarmManger的优点:咱们要知道虽然AlarmManager是一直存在可是JobScheduler是Android 5.x以后才有的。WorkManager的底层实现,会根据你的设备API的状况,自动选用JobScheduler, 或是AlarmManager来实现后台任务。
WorkManager对比AsyncTask, ThreadPool的优点:WorkManager里面的任务在应用退出以后还能够继续执行。AsyncTask, ThreadPool里面的任务在应用退出以后不会执行。
WorkManager适用于那些在应用退出以后任务还须要继续执行的需求(好比应用数据上报服务器的状况),对应那些在应用退出的以后任务也须要终止的状况就须要选择ThreadPool、AsyncTask来实现。git
1、WorkManager相关类介绍
想使用WorkManager组件库,第一步我们得先了解下WorkManager里面相关的几个类。github
1.一、Worker
Worker用于指定须要执行的具体任务。任务的具体逻辑在Worker里面写。
Worker是个抽象类。因此咱们须要继承并实现这个类在定义本身的任务。web
Worker类里面几个比较关键的函数:任务逻辑实现函数,任务输入数据的获取函数,任务输出数据的设置函数。数组
/**
* 任务逻辑
*@return 任务的执行状况,成功,失败,仍是须要从新执行
*/
@WorkerThread
public abstract @NonNull Worker.Result doWork();
/**
* 任务的输入数据,有的时候可能须要咱们传递参数进去,好比下载文件咱们须要传递文件路基进去,
* 在doWork()函数中经过getInputData()获取到咱们传递进来的参数
*@return Data参数
*/
public final @NonNull Data getInputData() {
return mExtras.getInputData();
}
/**
* 设置咱们任务输出结果
*@param outputData 结果
*/
public final void setOutputData(@NonNull Data outputData) {
mOutputData = outputData;
}
doWork()函数的返回值:
- Worker.Result.SUCCESS:任务执行成功。
- Worker.Result.FAILURE:任务执行失败。
- Worker.Result.RETRY:任务须要从新执行,须要配合WorkRequest.Builder里面的setBackoffCriteria()函数使用。服务器
1.二、WorkRequest
WorkRequest表明一个单独的任务,是对Worker任务的包装,一个WorkRequest对应一个Worker类。咱们能够经过WorkRequest来给Worker类添加约束细节,好比指定任务应该运行的环境,任务的输入参数,任务只有在有网的状况下执行等等。WorkRequest是一个抽象类,组件里面也给两个相应的子类:OneTimeWorkRequest(任务只执行一遍)、PeriodicWorkRequest(任务周期性的执行)。网络
WorkRequest.Builder: 建立WorkRequest对象的帮助类。
Constraints:指定任务运行的限制条件(例如,”仅当链接到网络时”)。使用Constraint.Builder来建立Constraints,并在建立WorkRequest以前把Constraints传给WorkRequest.Builder的setConstraints()函数。
WorkRequest里面经常使用函数介绍架构
/**
* 获取 WorkRequest对应的UUID
*/
public @NonNull UUID getId();
/**
* 获取 WorkRequest对应的UUID string
*/
public @NonNull String getStringId();
/**
* 获取WorkRequest对应的WorkSpec(包含了任务的一些详细信息)
*/
public @NonNull WorkSpec getWorkSpec();
/**
* 获取 WorkRequest对应的tag
*/
public @NonNull Set getTags();
public abstract static class Builder {
...
/**
* 设置任务的退避/重试策略。好比咱们在Worker类的doWork()函数返回Result.RETRY,让该任务又从新入队。
*/
public @NonNull B setBackoffCriteria(
@NonNull BackoffPolicy backoffPolicy,
long backoffDelay,
@NonNull TimeUnit timeUnit);
/**
* 设置任务的运行的限制条件,好比有网的时候执行任务,不是低电量的时候执行任务
*/
public @NonNull B setConstraints(@NonNull Constraints constraints);
/**
* 设置任务的输入参数
*/
public @NonNull B setInputData(@NonNull Data inputData);
/**
* 设置任务的tag
*/
public @NonNull B addTag(@NonNull String tag);
/**
* 设置任务结果保存时间
*/
public @NonNull B keepResultsForAtLeast(long duration, @NonNull TimeUnit timeUnit);
@RequiresApi(26)
public @NonNull B keepResultsForAtLeast(@NonNull Duration duration);
...
}
这里要稍微提下Builder的setBackoffCriteria()函数的使用场景,比较经常使用,通常当咱们任务执行失败的时候任务须要重试的时候会用到这个函数,在任务执行失败的时候Worker类的doWork()函数返回Result.RETRY告诉这个任务要重试。那重试的策略就是经过setBackoffCriteria()函数来设置的。BackoffPolicy有两个值LINEAR(每次重试的时间线性增长,好比第一次10分钟,第二次就是20分钟)、EXPONENTIAL(每次重试时间指数增长)。ide
1.三、WorkManager
管理任务请求和任务队列,咱们须要把WorkRequest对象传给WorkManager以便将任务编入队列。经过WorkManager来调度任务,以分散系统资源的负载。svg
WorkManager经常使用函数介绍
/**
* 任务入队
*/
public final void enqueue(@NonNull WorkRequest... workRequests);
public abstract void enqueue(@NonNull List extends WorkRequest> workRequests);
/**
* 链式结构的时候使用,从哪些任务开始。
* 好比咱们有A,B,C三个任务,咱们须要顺序执行。那咱们就能够WorkManager.getInstance().beginWith(A).then(B).then(C).enqueue();
*/
public final @NonNull WorkContinuation beginWith(@NonNull OneTimeWorkRequest...work);
public abstract @NonNull WorkContinuation beginWith(@NonNull List work);
/**
* 建立一个惟一的工做队列,惟一工做队列里面的任务不能重复添加
*/
public final @NonNull WorkContinuation beginUniqueWork(
@NonNull String uniqueWorkName,
@NonNull ExistingWorkPolicy existingWorkPolicy,
@NonNull OneTimeWorkRequest... work);
public abstract @NonNull WorkContinuation beginUniqueWork(
@NonNull String uniqueWorkName,
@NonNull ExistingWorkPolicy existingWorkPolicy,
@NonNull List work);
/**
* 容许将一个PeriodicWorkRequest任务放到惟一的工做序列里面去,可是当队列里面有这个任务的时候你的提供替换的策略。
*/
public abstract void enqueueUniquePeriodicWork(
@NonNull String uniqueWorkName,
@NonNull ExistingPeriodicWorkPolicy existingPeriodicWorkPolicy,
@NonNull PeriodicWorkRequest periodicWork);
/**
* 经过UUID取消任务
*/
public abstract void cancelWorkById(@NonNull UUID id);
/**
* 经过tag取消任务
*/
public abstract void cancelAllWorkByTag(@NonNull String tag);
/**
* 取消惟一队列里面全部的任务(beginUniqueWork)
*/
public abstract void cancelUniqueWork(@NonNull String uniqueWorkName);
/**
* 取消全部的任务
*/
public abstract void cancelAllWork();
/**
* 获取任务的WorkStatus。通常会经过WorkStatus来获取返回值,LiveData是能够感知WorkStatus数据变化的
*/
public abstract @NonNull LiveData getStatusById(@NonNull UUID id);
public abstract @NonNull LiveData> getStatusesByTag(@NonNull String tag);
/**
* 获取惟一队列里面全部的任务(beginUniqueWork)的WorkStatus
*/
public abstract @NonNull LiveData> getStatusesForUniqueWork(@NonNull String uniqueWorkName);
beginWith(),beginUniqueWork()两个函数开启的队列的惟一区别在于,队列里面的任务能不能重复。beginWith()开始的队列里面的任务是能够重复的,beginUniqueWork()开始的队列里面的任务是不能重复的。
1.四、WorkStatus
包含任务的信息。WorkManager为每一个WorkRequest对象提供一个LiveData(WorkManager经过getStatusById、getStatusesByTag、getStatusesForUniqueWork函数来获取)。LiveData持有一个WorkStatus对象。LiveData是能够感知数据变化的。经过观察这个LiveData,咱们能够肯定任务的当前状态,并在任务完成后得到返回值。WorkStatus里面就包含的东西很少就职务的id、tag、状态、返回值。
经过以下方式来监放任务的状态
// 获取到LiveData而后监听数据变化
WorkManager.getInstance().getStatusById(request.getId()).observe(this, new Observer() {
@Override
public void onChanged(@Nullable WorkStatus workStatus) {
if (workStatus == null) {
return;
}
if (workStatus.getState() == State.ENQUEUED) {
mTextOut.setText("任务入队");
}
if (workStatus.getState() == State.RUNNING) {
mTextOut.setText("任务正在执行");
}
if (workStatus.getState().isFinished()) {
Data data = workStatus.getOutputData();
mTextOut.setText("任务完成" + "-结果:" + data.getString("key_name", "null"));
}
}
});
1.五、Data
Data是用于来给Worker设置输入参数和输出参数的。举个例子,好比咱们须要去网络上下载图,那么须要给Worker传入下载地址(输入参数),在Worker执行成功以后咱们又须要获取到图片在本地的保持路径(输出参数)。这这个传入传出都是经过Data来实现的。Data是一个轻量级的容器(不能超过10KB),Data经过key-value的形式来保存信息。
2、WorkManager使用
前面讲了WorkManager里面经常使用的一些类,接下来就是WorkManager的使用了。
咱们把WorkManager的使用分为几个步骤:
- 继承Worker,处理任务的具体逻辑。
- OneTimeWorkRequest或者PeriodicWorkRequest包装Worker,设置Worker的一些约束添加,或者Worker的输入参数。
- 任务入队执行(若是是多个任务能够造成任务链在入队执行)。
- 监放任务的输出(LiveData的使用)。
2.一、任务的输入输出
有些时候,一个任务的执行,咱们可能须要从外部传入参数,在任务结束的时候须要把任务的结果告诉外部。好比咱们去网络上下载一个图片,那我们须要url地址(输入),在图片下载成功以后获取图片在本地的保存路径(输出)。
输入参数:想要给任务传递输入参数须要在WorkRequest包装Worker的经过WorkRequest.Builder的setInputData()函数设置输入参数。以后任务在执行过程当中能够经过getInputData()获取到传入的参数。
输出参数:任务执行过程当中若是想要结果传递给外界,须要在Worker中经过setOutputData()设置输出参数。以后若是想获取任务结果须要经过WorkManager.getInstance().getStatusById()或者WorkManager.getInstance().getStatusesByTag()先获取到LiveData。而后经过LiveData来感知任务数据的变化。
咱们经过一个简单的实力来看下任务有输入输出的状况应该怎么处理。
定义一个任务
public class InputOutputWorker extends Worker {
@NonNull
@Override
public Result doWork() {
try {
//模拟耗时任务
Thread.sleep(3000);
Data inputData = getInputData();
//获取到输入的参数,咱们又把输入的参数给outputData
Data outputData = new Data.Builder().putString("key_name", inputData.getString("key_name", "no data")).build();
setOutputData(outputData);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.SUCCESS;
}
}
OneTimeWorkRequest设置任务的输入,执行任务,任务的执行过程当中设置输出,LiveData感知任务结果
private void startWorker() {
// 定义一个OneTimeWorkRequest,而且关联InputOutputWorker。设置输入参数
Data inputData = new Data.Builder().putString("key_name", "江西高安").build();
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(InputOutputWorker.class)
.setInputData(inputData)
.build();
// 任务入队,WorkManager调度执行
WorkManager.getInstance().enqueue(request);
// 获取到LiveData而后监听数据变化
WorkManager.getInstance().getStatusById(request.getId()).observe(this, new Observer() {
@Override
public void onChanged(@Nullable WorkStatus workStatus) {
if (workStatus == null) {
return;
}
if (workStatus.getState() == State.ENQUEUED) {
mTextOut.setText("任务入队");
}
if (workStatus.getState() == State.RUNNING) {
mTextOut.setText("任务正在执行");
}
if (workStatus.getState().isFinished()) {
Data data = workStatus.getOutputData();
mTextOut.setText("任务完成" + "-结果:" + data.getString("key_name", "null"));
}
}
});
}
2.二、周期任务
WorkManager组件库里面提供了一个专门作周期性任务的类PeriodicWorkRequest。可是PeriodicWorkRequest类有一个限制条件最小的周期时间是15分钟。
private void startWorker() {
// 定义一个PeriodicWorkRequest,而且关联PeriodicWorker。任务15m循环(源码里面已经规定了最小时间间隔15分钟)
PeriodicWorkRequest request = new PeriodicWorkRequest.Builder(PeriodicWorker.class, 15, TimeUnit.MINUTES).build();
// 任务入队,WorkManager调度执行
WorkManager.getInstance().enqueue(request);
}
2.三、任务添加约束
有些状况下,有些任务可能须要添加额外的约束,好比只能在联网的状况下才能执行,只有在设备空闲的状况下才能执行等等。WorkManager里面全部的约束条件都是经过Constraints来实现的,Constraints也是经过Constraints.Builder()来实现的。
关于任务的约束要注意,可能咱们任务加入的那个时刻候没有知足约束的条件,任务没有执行。可是事后一旦约束条件知足以后任务会自动执行的。
Constraints经常使用函数-能够添加的限制以下
/**
* 是否在充电状态下执行任务
*/
public @NonNull Constraints.Builder setRequiresCharging(boolean requiresCharging);
/**
* 是否在设备空闲的时候执行
*/
@RequiresApi(23)
public @NonNull Constraints.Builder setRequiresDeviceIdle(boolean requiresDeviceIdle);
/**
* 指定网络状态执行任务
* NetworkType.NOT_REQUIRED:对网络没有要求
* NetworkType.CONNECTED:网络链接的时候执行
* NetworkType.UNMETERED:不计费的网络好比WIFI下执行
* NetworkType.NOT_ROAMING:非漫游网络状态
* NetworkType.METERED:计费网络好比3G,4G下执行。
*/
public @NonNull Constraints.Builder setRequiredNetworkType(@NonNull NetworkType networkType);
/**
* 在电量不足的是不是否能够执行任务
*/
public @NonNull Constraints.Builder setRequiresBatteryNotLow(boolean requiresBatteryNotLow);
/**
* 在存储容量不足时是否能够执行
*/
public @NonNull Constraints.Builder setRequiresStorageNotLow(boolean requiresStorageNotLow);
/**
* 当Uri有更新的时候是否执行任务
*/
@RequiresApi(24)
public @NonNull Constraints.Builder addContentUriTrigger(Uri uri, boolean triggerForDescendants);
咱们举一个简单的例子,好比咱们限制任务只有在wifi的状态下才能执行。
/**
* 启动约束任务
*/
private void startWorker() {
// 设置只有在wifi状态下才能执行
Constraints constraints = new Constraints.Builder().setRequiredNetworkType(NetworkType.UNMETERED).build();
// 设置约束条件
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(ConstraintsWorker.class).setConstraints(constraints).build();
// 任务入队,WorkManager调度执行
WorkManager.getInstance().enqueue(request);
}
2.三、任务取消
每一个任务都有本身独特的UUID,咱们能够经过任务的UUID找到任务,而后取消他。除了UUID的方式,咱们还能够给任务添加tag,而后经过tag来取消任务(能够给多个任务添加同一个tag,同时取消)。
咱们简单的实现一个经过tag取消任务的例子
/**
* 给任务设置tag
*/
private void startWorker() {
// 给任务设置tag->cancel
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(ConstraintsWorker.class).addTag("cancel").build();
// 任务入队,WorkManager调度执行
WorkManager.getInstance().enqueue(request);
}
/**
* 经过tag取消任务
*/
private void cancelWorker() {
WorkManager.getInstance().cancelAllWorkByTag("cancel");
}
2.四、链式任务
有时候咱们想让某些按照特定的顺序行来执行。WorkManager容许咱们建立和排队多个任务的工做序列,以及它们应该以什么顺序运行。这个就是链式任务了。
任务链里面的任何一个任务返回WorkerResult.FAILURE,则整个任务链终止。
链式任务的关键在WorkContinuation,经过WorkContinuation来整理好队列(是顺序执行,仍是组合执行)而后入队执行。
WorkContinuation里面经常使用函数介绍
/**
* 顺序执行任务,
* 若是then的参数指定了多个任务那么这些任务的执行顺序是没有规律的,可是必定要等这个then函数参数里面全部任务都执行完了才会去执行下一个then的任务
* 任务任务返回Worker.WorkerResult.FAILURE整个则整个任务链结束
*/
public final @NonNull WorkContinuation then(@NonNull OneTimeWorkRequest... work);
public abstract @NonNull WorkContinuation then(@NonNull List work);
/**
* 这些都是static函数哦,用于组合任务
*/
public static @NonNull WorkContinuation combine(@NonNull WorkContinuation... continuations);
public static @NonNull WorkContinuation combine(@NonNull List continuations);
public static @NonNull WorkContinuation combine(@NonNull OneTimeWorkRequest work,
@NonNull WorkContinuation... continuations);
public static @NonNull WorkContinuation combine(@NonNull OneTimeWorkRequest work,
@NonNull List continuations);
/**
* 获取任务链中全部任务的LiveData,用于监放任务链里面任务的结果
*/
public abstract @NonNull LiveData> getStatuses();
/**
* 任务链中的任务入队,开始执行。
*/
public abstract void enqueue();
2.4.一、任务顺序执行
任务顺序执行,WorkContinuation的then()函数的使用。假设咱们有A,B,C三个任务须要按顺序执行。
/**
* A,B,C三个任务顺序执行
*/
private void startWorker() {
// A
OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(OrderWorkerA.class).build();
// B
OneTimeWorkRequest requestB = new OneTimeWorkRequest.Builder(OrderWorkerB.class).build();
// C
OneTimeWorkRequest requestC = new OneTimeWorkRequest.Builder(OrderWorkerC.class).build();
// 任务入队,WorkManager调度执行
WorkManager.getInstance().beginWith(requestA).then(requestB).then(requestC).enqueue();
}
为了把顺序任务说的更加完全,咱们在来一个例子。
WorkManager.getInstance()
// First, run all the A tasks (in parallel):
.beginWith(workA1, workA2, workA3)
// ...when all A tasks are finished, run the single B task:
.then(workB1, workB2)
// ...then run the C tasks (in any order):
.then(workC1, workC2)
.enqueue();
上诉代码中beginWith函数里面的workA1, workA2, workA3三个任务是平行(同时)执行的,并且要等workA1, workA2, workA3都执行完才能作下一步the里的任务。then(workB1, workB2)里面的workB1,workB2的执行顺序是没有规律的,可是必定要等到workB1,workB2都执行玩才能执行下一步的then里面的任务。workC1, workC2的执行顺序也是没有规律的。
2.4.二、组合任务
想要组合任务,就须要用到WorkContinuation的combine()函数了。咱们用一个很是见到的任务来讲明组合任务的执行。好比咱们想要实现以下图所示的链试效果。
上图对应代码以下
/**
* 组合任务
*/
private void startWorker() {
OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(ConbineWorkerA.class).build();
OneTimeWorkRequest requestB = new OneTimeWorkRequest.Builder(ConbineWorkerB.class).build();
OneTimeWorkRequest requestC = new OneTimeWorkRequest.Builder(ConbineWorkerC.class).build();
OneTimeWorkRequest requestD = new OneTimeWorkRequest.Builder(ConbineWorkerD.class).build();
OneTimeWorkRequest requestE = new OneTimeWorkRequest.Builder(ConbineWorkerE.class).build();
//A,B任务链
WorkContinuation continuationAB = WorkManager.getInstance().beginWith(requestA).then(requestB);
//C,D任务链
WorkContinuation continuationCD = WorkManager.getInstance().beginWith(requestC).then(requestD);
//合并上面两个任务链,在接入requestE任务,入队执行
WorkContinuation.combine(continuationAB, continuationCD).then(requestE).enqueue();
}
2.4.三、任务链中任务数据流(每一个任务的输入输出)
在任务链中,咱们可能会有这样的需求,任务之间的数据是相互依赖的,下一个任务须要上一个任务的输出数据。这种状况咱们就称之为任务链中任务的数据流。其实强大的WorkManager已经帮咱们设计好了。WorkManager会把上一个任务的输出自动做为下一我的任务的输入。
2.4.3.一、顺序任务的数据流
由于WorkManager设计的时候已经帮咱们设计好了上一任务的输出会自动做为下一个任务的输入。因此顺序任务的数据流是很是好处理的。上一个任务调用setOutputData()返回其结果,下一个任务调用getInputData()来获取上一个任务的结果。咱们用一个简单的实例来讲明。A,B,C三个顺序任务。A任务输出10,B任务获得A任务的值再乘以10,最后把结果给到C任务。咱们来看下这种状况下的代码应该怎么写。
A任务
/**
* A任务输出10
*/
public class StreamThenWorkerA extends Worker {
@NonNull
@Override
public Result doWork() {
Data data = new Data.Builder().putInt("a_out", 10).build();
setOutputData(data);
return Result.SUCCESS;
}
}
B任务
/**
* 获得A任务的输出在乘以10,作为输出
*/
public class StreamThenWorkerB extends Worker {
@NonNull
@Override
public Result doWork() {
//先获得A任务的输出值
Data inputData = getInputData();
int a_out = inputData.getInt("a_out", 0);
//把A任务的输出×10在给到C任务
Data data = new Data.Builder().putInt("b_out", a_out * 10).build();
setOutputData(data);
return Result.SUCCESS;
}
}
C任务
/**
* 只是作一个简单的打印
*/
public class StreamThenWorkerC extends Worker{
@NonNull
@Override
public Result doWork() {
Data inputData = getInputData();
int b_out = inputData.getInt("b_out", 0);
//获取到B任务的输出,咱们只是作一个简单的输出。
Log.d("tuacy", "value = " + b_out);
return Result.SUCCESS;
}
}
执行任务
/**
* 顺序任务的数据流
* A,B,C三个任务。A,输出10,B任务获得A任务的值×10,最后给到C任务。
*/
private void startThenWorker() {
OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(StreamThenWorkerA.class).build();
OneTimeWorkRequest requestB = new OneTimeWorkRequest.Builder(StreamThenWorkerB.class).build();
OneTimeWorkRequest requestC = new OneTimeWorkRequest.Builder(StreamThenWorkerC.class).build();
WorkManager.getInstance().beginWith(requestA).then(requestB).then(requestC).enqueue();
}
2.4.3.二、组合任务的数据流
组合任务的数据流稍稍复杂一点,由于涉及到多个任务的输出同时做为一个任务的输入,这个时候多个任务的输出须要合并一个输入。这个时候就会有合并规则一说了(不一样的任务中有相同的key应该怎么处理),WorkManager经过OneTimeWorkRequest.Builder类的setInputMerger()函数来指定多个任务输入流的合并规则。参数是继承自InputMerger的类。InputMerger是一个抽象类,WorkManager也给咱们提供了两种合并规则:ArrayCreatingInputMerger、OverwritingInputMerger。
ArrayCreatingInputMerger:全部key对应的value都会放到数组里面,有相同的key的话,数组慢慢扩大。好比有A、B两个任务的输出须要组合到一块儿。A任务输出里面有一个key:a_key->100。B任务里面有两个key(有个key和A任务是相同的):b_key->100、a_key->200。最后经过ArrayCreatingInputMerger规则组合的结果是:a_key对应一个数组,数组里面有两个元素100和200、b_key也对应一个数组,里面只有一个元素100。这个时候在下一个任务中想要获取合并以后的输入必须使用getIntArray(),由于如今key对应的value是一个数组了。
OverwritingInputMerger:若是有相同的key,直接覆盖。我经过测试发现OverwritingInputMerger没效果,表现形式和ArrayCreatingInputMerger同样
咱们仍是用一个简单的例子来讲明组合任务的数据流,咱们有A,B,C三个任务。A,B任务合并再执行C任务。在C任务中获取A,B两个任务的输出。
A任务的输出中只有一个key: a_key -> 100
/**
* A任务的输出中只有一个key: a_key -> 100
*/
public class StreamCombineWorkerA extends Worker {
@NonNull
@Override
public Result doWork() {
Data data = new Data.Builder().putInt("a_key", 100).build();
setOutputData(data);
return Result.SUCCESS;
}
}
B任务的输出中有两个key:b_key -> 100、a_key -> 200,有个key在A任务中也出现了
/**
* B任务的输出中有两个key:b_key -> 100、a_key -> 200
* 有个key在A任务中也出现了
*/
public class StreamCombineWorkerB extends Worker {
@NonNull
@Override
public Result doWork() {
Data data = new Data.Builder().putInt("b_key", 100).putInt("a_key", 200).build();
setOutputData(data);
return Result.SUCCESS;
}
}
C任务只是简单的获取A,B任务的输出
/**
* 在C任务中获取到A,B任务的输出。
*
*/
public class StreamCombineWorkerC extends Worker {
@NonNull
@Override
public Result doWork() {
Data data = getInputData();
// 注意;这里我用的是getIntArray
int[] aKeyValueList = data.getIntArray("a_key");
int[] bKeyValueList = data.getIntArray("b_key");
Log.d("tuacy", "a_key = " + aKeyValueList[0]);
Log.d("tuacy", "b_key = " + bKeyValueList[0]);
return Result.SUCCESS;
}
}
启动组合任务,调用setInputMerger(
OverwritingInputMerger.class)来设置合并规则
private void startCombineWorker() {
OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(StreamCombineWorkerA.class).build();
OneTimeWorkRequest requestB = new OneTimeWorkRequest.Builder(StreamCombineWorkerB.class).build();
// 设置合并规则OverwritingInputMerger
OneTimeWorkRequest requestC = new OneTimeWorkRequest.Builder(StreamCombineWorkerC.class).setInputMerger(
OverwritingInputMerger.class).build();
//A任务链
WorkContinuation continuationA = WorkManager.getInstance().beginWith(requestA);
//B任务链
WorkContinuation continuationB = WorkManager.getInstance().beginWith(requestB);
//合并上面两个任务链,在接入requestE任务,入队执行
WorkContinuation continuation = WorkContinuation.combine(continuationA, continuationB).then(requestC);
continuation.enqueue();
}
2.4.四、惟一工做队列
咱们上面例子中全部的链试任务的队列都是经过WorkManager.getInstance().beginWith()来建立的。这种方式建立的链试任务没啥限制条件,任务随便怎么入队。要是某些场景咱们须要同一个任务不能重复入队怎么办。这个时候就须要惟一工做队列了。
WorkManager容许咱们建立一个惟一的工做队列。惟一工做队列指的是这个队列中任务不能重复入队。WorkManager中经过beginUniqueWork()来建一个惟一队列。每一个惟一工做队列建立的时候都必须指定一个队列名字,同时还得指定ExistingWorkPolicy当WorkManager里面已经有一个相同的惟一队列时候的处理方式。ExistingWorkPolicy有三个值:REPLACE(取消现有的序列并将其替换为新序列)、KEEP(保持现有顺序并忽略新请求)、APPEND(将新序列附加到现有序列,在现有序列的最后一个任务完成后运行新序列的第一个任务)。
若是在惟一工做队列中屡次加入同一个任务,程序会异常退出。
/**
* A,B,C三个任务加入到惟一工做队列中去
*/
private void startWorker() {
// A
OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(OrderWorkerA.class).build();
// B
OneTimeWorkRequest requestB = new OneTimeWorkRequest.Builder(OrderWorkerB.class).build();
// C
OneTimeWorkRequest requestC = new OneTimeWorkRequest.Builder(OrderWorkerC.class).build();
// 任务入队,WorkManager调度执行
WorkManager.getInstance().beginUniqueWork("unique", ExistingWorkPolicy.KEEP, requestA)
.then(requestB)
.then(requestC)
.enqueue();
}
关于WorkManager的任务就讲这么写,若是你们在使用过程当中有什么疑问,欢迎留言。最后给出本文涉及到的实例下载地址https://github.com/tuacy/WorkManagerDev。