Android WorkManager★★★

1.WorkManager

Google发布的Android Jetpack架构中,有一个专门用于安排和管理后台任务的库WorkManager 。Android已经有很多管理后台任务的类了,比如JobScheduler、AlarmManger,或者AsyncTask、 ThreadPool。那WorkManager的优势在哪里?

①WorkManager对比JobScheduler、AlarmManger的优势:

虽然AlarmManager是一直存在,但是JobScheduler是Android 5.x之后才有的。WorkManager的底层实现,会根据你的设备API的情况,自动选用JobScheduler, 或是AlarmManager来实现后台任务。

②WorkManager对比AsyncTask、ThreadPool的优势:

WorkManager里面的任务在应用退出之后还可以继续执行。AsyncTask,、ThreadPool里面的任务在应用退出之后不会执行。

因此,WorkManager适用于那些在应用退出之后任务还需要继续执行的需求(比如应用数据上报服务器的情况),对于那些在应用退出的之后任务也需要终止的情况就需要选择ThreadPool、AsyncTask来实现了。

WorkManager是用于后台工作的架构组件,需要兼顾机会和有保证的执行。机会性执行意味着WorkManager将尽快完成您的后台工作;有保证的执行意味着即使在离开应用程序的情况下,WorkManager也会兼顾各种情况下开始逻辑工作。

①使用WorkManager API可以轻松地调度即使在应用退出或设备重启时仍应运行的可延迟异步任务。

②用统一的方法解决绝大部分后台问题。

③很容易计划、取消和管理多个工作顺序和平行的执行。

WorkManager是一个简单但非常灵活的库,它具有许多其他优点。这些包括:

①支持异步一次性和定期任务

②支持网络条件,存储空间和充电状态等约束

链接复杂的工作请求,包括并行运行工作

③一个工作请求的输出用作下一个工作的输入

④LiveData支持可轻松在UI中显示工作请求状态

使用WorkManager需要下面的gradle依赖项:

implementation "androidx.work:work-runtime:2.3.3"

整个WorkManager的执行流程:

① 给WorkManager发送工作请求WorkRequest。

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

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

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

2.WorkManager的核心类

(1)Worker

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

Worker类里面几个比较关键的函数:任务逻辑实现函数、任务输入数据的获取函数、任务输出数据的设置函数。

//任务逻辑。返回任务的执行情况:成功、失败、还是需要重新执行

@WorkerThread
public abstract Worker.Result doWork();

//任务的输入数据,有时候可能需要传递参数进去,比如下载文件需要传递文件路径进去,在doWork()函数中通过getInputData()获取到传递进来的参数

public final Data getInputData() {
    return mExtras.getInputData();
}

//设置任务输出结果

public final void setOutputData(Data outputData){

    mOutputData = outputData;

}

doWork()函数的返回值:

Worker.Result.SUCCESS:任务执行成功。

Worker.Result.FAILURE:任务执行失败。

Worker.Result.RETRY:任务需要重新执行,需要配合WorkRequest.Builder里面的setBackoffCriteria()函数使用。

(2)WorkRequest

WorkRequest代表一个单独的任务,是对Worker任务的包装,一个WorkRequest对应一个Worker类。可以通过WorkRequest给Worker类添加约束细节,比如指定任务应该运行的环境、任务的输入参数、任务只有在有网的情况下执行等等。WorkRequest是一个抽象类,组件里面也给了两个相应的子类:OneTimeWorkRequest(任务只执行一遍)、PeriodicWorkRequest(任务周期性的执行)。每个WorkRequest都有一个自动生成的唯一ID, 可以使用该ID来执行诸如取消排队的任务或获取任务状态等操作。

与WorkerRequest相关的有如下两个类:

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

② Constraints:指定任务运行时的限制(例如,“仅在连接到网络时才能运行”)。使用Constraint.Builder创建Constraints,并在调用WorkRequest.Builder的build()方法之前把Constraints传给WorkRequest的setConstraints()函数。

Constraints常用函数-可以添加的限制如下:

//是否在充电状态下执行任务

public Constraints.Builder setRequiresCharging( boolean requiresCharging);

//是否在设备空闲的时候执行

public Constraints.Builder setRequiresDeviceIdle( boolean requiresDeviceIdle);

//指定网络状态执行任务。NetworkType.NOT_REQUIRED:对网络没有要求;NetworkType.CONNECTED:网络连接的时候执行;NetworkType.UNMETERED:不计费的网络比如WIFI下执行;NetworkType.NOT_ROAMING:非漫游网络状态;NetworkType.METERED:计费网络比如3G,4G下执行。

public Constraints.Builder setRequiredNetworkType(NetworkType networkType);

//在电量不足的时候是否可以执行任务

public Constraints.Builder setRequiresBatteryNotLow( boolean requiresBatteryNotLow);

//在存储容量不足时是否可以执行

public Constraints.Builder setRequiresStorageNotLow( boolean requiresStorageNotLow);

//当Uri有更新的时候是否执行任务

public Constraints.Builder addContentUriTrigger( Uri uri, boolean triggerForDescendants);

WorkRequest里有一个与重试相关的方法:

//设置任务的退避/重试策略。比如在Worker类的doWork()函数返回Result.RETRY,让该任务又重新入队。

public B setBackoffCriteria(BackoffPolicy backoffPolicy, long backoffDelay,TimeUnit timeUnit);

当任务执行失败需要重试的时候会用到这个函数,在任务执行失败的时候Worker类的doWork()函数返回Result.RETRY告诉这个任务要重试。那重试的策略就是通过setBackoffCriteria()函数来设置的。BackoffPolicy有两个值LINEAR(每次重试的时间线性增加,比如第一次10分钟,第二次就是20分钟)、EXPONENTIAL(每次重试时间指数增加)。

(3)WorkManager

管理任务请求和任务队列,需要把WorkRequest对象传给WorkManager以便将任务编入队列。通过WorkManager来调度任务,以分散系统资源的负载,同时会遵循前面设置的对任务的约束条件。

WorkManager常用函数:

//任务入队

public final void enqueue(WorkRequest... workRequests);

public abstract void enqueue(List<? extends WorkRequest> workRequests);

//链式结构的时候使用,从哪些任务开始。比如有A,B,C三个任务需要顺序执行。就可以WorkManager.getInstance().beginWith(A).then(B).then(C).enqueue();

public final WorkContinuation beginWith(OneTimeWorkRequest...work);

public abstract WorkContinuation beginWith(List<OneTimeWorkRequest> work);

//创建一个唯一的工作队列,唯一工作队列里面的任务不能重复添加

public final WorkContinuation beginUniqueWork( String uniqueWorkName, ExistingWorkPolicy existingWorkPolicy, OneTimeWorkRequest... work);

public abstract WorkContinuation beginUniqueWork(String uniqueWorkName, ExistingWorkPolicy existingWorkPolicy, List<OneTimeWorkRequest> work);

//允许将一个PeriodicWorkRequest任务放到唯一的工作序列里面去,但是当队列里面有这个任务的时候需要提供替换的策略。

public abstract void enqueueUniquePeriodicWork (String uniqueWorkName, ExistingPeriodicWorkPolicy existingPeriodicWorkPolicy,PeriodicWorkRequest periodicWork);

//通过UUID取消任务

public abstract void cancelWorkById(UUID id);

//通过tag取消任务

public abstract void cancelAllWorkByTag(String tag);

//取消唯一队列里面所有的任务(beginUniqueWork)

public abstract void cancelUniqueWork(String uniqueWorkName);

//取消所有的任务

public abstract void cancelAllWork();

//获取任务的WorkStatus。一般通过WorkStatus来获取返回值,LiveData是可以感知WorkStatus数据变化的

public abstract LiveData<WorkStatus> getStatusById(UUID id);

public abstract LiveData<List<WorkStatus>> getStatusesByTag(String tag);

//获取唯一队列里面所有的任务(beginUniqueWork)的WorkStatus

public abstract LiveData<List<WorkStatus>> getStatusesForUniqueWork(String uniqueWorkName);

beginWith(),beginUniqueWork()两个函数开启的队列的唯一区别在于,队列里面的任务能不能重复。beginWith()开始的队列里面的任务是可以重复的,beginUniqueWork()开始的队列里面的任务是不能重复的。

(4)WorkStatus

包含任务的信息。WorkManager为每个WorkRequest对象提供一个LiveData (WorkManager通过getStatusById、getStatusesByTag、getStatusesForUniqueWork函数来获取)。LiveData持有一个WorkStatus对象。LiveData是可以感知数据变化的。通过观察这个LiveData,可以确定任务的当前状态,并在任务完成后通过调用WorkStatus的getOutputData()方法获得返回值。WorkStatus里面包含的东西不多,就任务的id、tag、状态、返回值。

//获取到LiveData然后监听数据变化

WorkManager.getInstance().getStatusById( request.getId()).observe(this, new Observer<WorkStatus>() {

    @Override

    public void onChanged(WorkStatus workStatus) {

    }

});

(5)Data

Data是用于来给Worker设置输入参数和输出参数的。比如需要去网络上下载图,就需要给Worker传入下载地址(输入参数),在Worker执行成功之后又需要获取到图片在本地的保持路径(输出参数)。这个传入传出都是通过Data来实现的。Data是一个轻量级的容器(不能超过10KB),Data通过key-value的形式来保存信息。

要给任务传递输入参数需要在WorkRequest包装Worker的通过WorkRequest.Builder的setInputData()函数设置输入参数。之后任务在执行过程中可以通过getInputData()获取到传入的参数。

任务执行过程中如果想要把结果传递给外界,需要在Worker中通过setOutputData()设置输出参数。之后如果想获取任务结果需要通过WorkManager.getInstance().getStatusById()或者WorkManager.getInstance().getStatusesByTag()先获取到LiveData。然后通过LiveData来感知任务数据的变化。

3.WorkManager使用

WorkManager的使用分为几个步骤:

①继承Worker,处理任务的具体逻辑。

②OneTimeWorkRequest或者PeriodicWorkRequest包装Worker,设置Worker的一些约束添加,或者Worker的输入参数。

③任务入队执行(如果是多个任务可以形成任务链在入队执行)。并可以监听任务的输出(LiveData的使用)。

步骤一:创建Worker

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

public class InputOutputWorker extends Worker {

    @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.FAILURE;

        }

        return Result.SUCCESS;

    }

}

步骤二:创建Constraints和WorkerRequest

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

关于任务的约束,要注意,可能任务加入的那个时刻候没有满足约束的条件,任务没有执行。但是过后一旦约束条件满足之后任务会自动执行的。

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

Constraints constraints = new Constraints.Builder()

    .setRequiresDeviceIdle(true)

    .setRequiresCharging(true)

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

    .build();

//输入数据

Data inputData = new Data.Builder( ).putString("key_name", "河北哒").build();

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

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

    .setConstraints(constraints)

    .setInputData(inputData)  //设置输入数据,在worker类里可以获取到这里设置的数据

    .build()

步骤三:WorkManager运行任务并监听结果

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

WorkManager.getInstance().enqueue(workRequest);

//通过WorkerRequest的id来获取LiveData

WorkManager.getInstance().getStatusById(request.getId()).observe(this, new Observer<WorkStatus>() {

    @Override

    public void onChanged(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"));

        }

    }

});

WorkerRequest还支持其他操作:

①取消任务

WorkerRequest排入队列后,可以取消任务。每个任务都有自己独特的UUID,可以通过任务的UUID找到任务,然后取消他。除了UUID的方式,还可以给任务添加tag,然后通过tag来取消任务(可以给多个任务添加同一个tag,同时取消)。

WorkManager会尽最大努力取消任务,但这本质上是不确定的,有可能在尝试取消任务时,任务已经在运行了或者已经运行完成了。

//通过UUID取消任务

UUID compressionWorkId = compressionWork.getId();

WorkManager.getInstance().cancelWorkById(compressionWorkId);

//通过tag取消任务
private void startWorker() {

   // 给任务设置tag->cancel
    OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(ConstraintsWorker.class).addTag("cancel").build();

    // 任务入队,WorkManager调度执行
    WorkManager.getInstance().enqueue( request);

}

private void cancelWorker() {

    WorkManager.getInstance().cancelAllWor kByTag("cancel"); //通过tag取消任务

}

②周期任务

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);

}

③链式任务

有时候可能需要按特定顺序运行多个任务。WorkManager允许创建和排队多个任务的工作序列,以及它们应该以什么顺序运行,这就是链式任务。

任务链里面的任何一个任务返回WorkerResult.FAILURE,则整个任务链终止。链式任务的关键在WorkContinuation,通过WorkContinuation来整理好队列(是顺序执行,还是组合执行),然后入队执行。

WorkContinuation里面常用函数介绍:

//顺序执行任务,如果then的参数指定了多个任务,那这些任务的执行顺序是没有规律的,但是一定要等这个then函数参数里面所有任务都执行完了才会去执行下一个then的任务。任一个任务返回Worker.WorkerResult.FAILURE整个则整个任务链结束

public final WorkContinuation then( OneTimeWorkRequest... work);

public abstract WorkContinuation then( List<OneTimeWorkRequest> work);

// 这些都是static函数,用于组合任务

public static WorkContinuation combine( WorkContinuation... continuations);

public static WorkContinuation combine( List<WorkContinuation> continuations);

public static WorkContinuation combine( OneTimeWorkRequest work, WorkContinuation... continuations);

public static WorkContinuation combine( OneTimeWorkRequest work, List<WorkContinuation> continuations);

//获取任务链中所有任务的LiveData,用于监听任务链里面任务的结果

public abstract LiveData<List<WorkStatus>> getStatuses();

//任务链中的任务入队,开始执行。

public abstract void enqueue();

例如,假设有三个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的结合。

④唯一工作队列

上面例子中所有的链试任务的队列都是通过WorkManager.getInstance().beginWith()来创建的。这种方式创建的链试任务没啥限制条件,任务随便怎么入队。要是某些场景需要同一个任务不能重复入队怎么办?这时候就需要唯一工作队列了。

WorkManager允许创建一个唯一的工作队列。唯一工作队列指的是这个队列中任务不能重复入队。WorkManager中通过beginUniqueWork()来建一个唯一队列。每个唯一工作队列创建的时候都必须指定一个队列名字,同时还得指定ExistingWorkPolicy。

当创建一个新的唯一工作序列时,WorkManager如果已经有一个待处理的序列具有相同的名字会根据传入的策略标志不同有如下三种操作:

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

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

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

如果在唯一工作队列中多次加入同一个任务,程序会异常退出。

//A,B,C三个任务加入到唯一工作队列中去

private void startWorker() {   

    OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(OrderWorkerA.class).build(); // A

    OneTimeWorkRequest requestB = new OneTimeWorkRequest.Builder(OrderWorkerB.class).build(); //B

    OneTimeWorkRequest requestC = new OneTimeWorkRequest.Builder(OrderWorkerC.class).build(); //C

    // 任务入队,WorkManager调度执行

    WorkManager.getInstance().beginUniqueWork( "unique", ExistingWorkPolicy.KEEP, requestA)

            .then(requestB)

           .then(requestC)

           .enqueue();

}

⑤设置标签

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

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

    .setConstraints(myConstraints)

    .addTag("myWork")

    .build();

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

⑥任务链中任务数据流(每个任务的输入输出)

在任务链中,可能会有这样的需求,任务之间的数据是相互依赖的,下一个任务需要上一个任务的输出数据。这种情况我们就称之为任务链中任务的数据流。其实强大的WorkManager已经帮我们设计好了。WorkManager会把上一个任务的输出自动作为下一个人任务的输入。

1)顺序任务的数据流

 因为WorkManager设计的时候已经设计好了上一任务的输出会自动作为下一个任务的输入。所以顺序任务的数据流是非常好处理的。上一个任务调用setOutputData()返回其结果,下一个任务调用getInputData()来获取上一个任务的结果。

用一个简单的实例来说明。现在有A,B,C三个顺序任务,A任务输出10,B任务得到A任务的值再乘以10,最后把结果给到C任务。

//A任务(输出10)

public class StreamThenWorkerA extends Worker {

    @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 {

    @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{

    @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;

    }

}

执行任务:

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)组合任务的数据流组合任务的数据流稍稍复杂一点,因为涉及到多个任务的输出同时作为一个任务的输入,这个时候多个任务的输出需要合并一个输入。这个时候就会有合并规则一说了(不同的任务中有相同的key应该怎么处理)。

比如遇到如下情况就有个问题,一个work有多个前置work:

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

如上图所示,当一个任务有两个前置任务时,这时直接使用前面输出的output Data,就要给它设置InputMerger策略来指定多个任务输入流的合并规则了。

InputMerger是一个抽象类,WorkManager也提供了两种合并规则:ArrayCreatingInputMerger、OverwritingInputMerger。

(1)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

 (2)ArrayCreatingInputMerger

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

所有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是一个数组了。

合并两组数据:

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();

还是用一个简单的例子来说明组合任务的数据流,现在有A,B,C三个任务。A,B任务合并再执行C任务,在C任务中获取A,B两个任务的输出。

//A任务(输出中只有一个key: a_key -> 100)

public class StreamCombineWorkerA extends Worker {

    @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任务中也出现了)

public class StreamCombineWorkerB extends Worker {

    @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任务的输出)

public class StreamCombineWorkerC extends Worker {

    @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();

}

猜你喜欢

转载自blog.csdn.net/zenmela2011/article/details/123790736