Android Jetpack——WorkManager

一、WorkManager简介

我们先来看下官网是对WorkManager如何介绍的。

The WorkManager API makes it easy to schedule deferrable, 
asynchronous tasks that are expected to run even if the app exits or device restarts.

简单翻译:WorkManager API可以让你更加容易的管理一些后台任务,即使你的应用是退出或者设备重启的状态。
其实就是"管理一些要在后台工作的任务, -- 即使你的应用没启动也能保证任务能被执行".

WorkManager is intended for tasks that are deferrable—that is, 
not required to run immediately—and required to run reliably even if the app exits or the device restarts. For example:
Sending logs or analytics to backend services
Periodically syncing application data with a server

WorkManager适用于任务的延迟执行,不需要立即执行,并且可以可靠的执行即使应用退出了或者设备重启。例如:
向后端服务发送日志分析
定期与服务器同步应用程序数据

WorkManager is not intended for in-process background work that can safely 
be terminated if the app process goes away or for tasks that require immediate execution.

WorkManager 不适用那些在应用进程消失的时候,可以安全被终止的后台任务。
翻译比较拗口,也就是说,WorkManager 并不会因为应用进程的结束而被终止。

针对官网对WorkManager介绍的小结:
在应用的开发中,WorkManager可以作为管理任务的组建,并且和传统Service后台任务还是有区别的,WorkManager不会因为应用进程的消失而被终止,即使设备被重启。而Service 是做不到的。Service 更加适用于应用内的后台任务。并且WorkManager可以设置延迟执行,不需要马上执行,也就是我们常说的定时任务。
举个例子:当我们要开发一款安全检查软件的时候,需要在施工现场拍照,拍视频,并把现场的情况录入到系统中,但是往往现场的网络是差的,这个时候我们可以先把数据保存到本地数据库,然后通过WorkManager设置定时任务,在有网的情况上传数据。

二、使用WorkManager调度任务

当WorkManager在应用程序中执行某一个任务,WorkManager则会开一个线程来执行当前任务,假如应用没有启动,WorkManager会选择合适的方式来安排这个后台任务。我们不需要关心选择的是什么方式,我们可以将任务交给WorkManager,让WorkManager选择一个合适的方式来安排任务的执行。
此外WorkManager还提供了某些高级的功能,比如我们可以建立一连串的任务让WorkManager执行,WorkManager会按照顺序执行这些任务。我们还可以观察任务的LiveData,检查任务的返回值,如果需要在UI做相应的展示,这个特性是非常有用的。

三、WorkManager的集成

我们需要添加WorkManager的依赖。

    def work_version = "1.0.0-alpha01"

    implementation "android.arch.work:work-runtime:$work_version" // use -ktx for Kotlin

    // optional - Firebase JobDispatcher support
    implementation "android.arch.work:work-firebase:$work_version"

    // optional - Test helpers
    androidTestImplementation "android.arch.work:work-testing:$work_version"

四、主要相关类的介绍

Worker:

Worker是具体要执行任务的类,我们的任务就是在Worker中执行

WorkRequest:

把Worker包装成WorkRequest,并加入队列,WorkRequest中多了一些属性,比如:
1、ID 任务的唯一性
2、何时执行任务
3、执行有没有环境的限制(如只有有网的时候才执行)
4、任务执行链条(某个任务执行完,才到我执行)

WorkManager

WorkManager负责把WorkRequest加入到队列,并管理WorkRequest

WorkStatus

WorkStatus包含了某个任务的相关信息,WorkManager为每个WorkRequest提供了一个LiveData对象,而
LiveData对象中包含了WorkStatus信息,通过观察LiveData对象的变化获取WorkStatus的信息,
以便确认当前任务的状态。

五、典型例子

下面我们通过一个例子来介绍以上各个类的使用方式。
假如我们要开发一个图库软件,需要定时对图库里的图片进行压缩。那么我们知道,我们的任务是压缩。
1、首先我们创建Worker的实现类,我们在doWork方法中执行压缩的任务。

package com.example.administrator.workmanager;

import android.support.annotation.NonNull;
import android.util.Log;

import androidx.work.Worker;

public class MyWorker extends Worker {
    private static final String TAG = "MyWorker";

    @NonNull
    @Override
    public WorkerResult doWork() {
        //压缩方法
        compress();
        return WorkerResult.SUCCESS;
    }

    /**
     * 压缩图片任务
     */
    private void compress() {
        Log.e(TAG, "compress: 对图片进行压缩" );
    }
}

2、把我们创建的Worker包装成WorkRequest,在这我们使用系统的OneTimeWorkRequest

     //把 MyWorker 包装成 OneTimeWorkRequest
        OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MyWorker.class).build();

我们看下OneTimeWorkRequest源码

/**
 * A class that represents a request for non-repeating work.
 */

public final class OneTimeWorkRequest extends WorkRequest {

大概意思是指OneTimeWorkRequest包装的任务不需要重复执行,只执行一次。

3、我们通过WorkManager把OneTimeWorkRequest加入到队列

 WorkManager.getInstance().enqueue(request);

在条件允许的情况下,如果我们没有指定任何约束,WorkManager会立即执行我们的任务。
我们可以看到控制台,我们压缩方法执行了

MyWorker: compress: 对图片进行压缩

检查任务状态

以上我们通过WorkManager完成了对任务的执行,我们来看下如何检查任务的状态

 WorkManager.getInstance().getStatusById(request.getId()).observe(this, new Observer() {
            @Override
            public void onChanged(@Nullable WorkStatus workStatus) {
                if(workStatus!=null&&workStatus.getState().isFinished()){
                    Log.e(TAG, "onChanged: 任务完成了" ); 
                }
            }
        });

我们通过request的id获取到任务的状态,当任务完成的时候,
workStatus.getState().isFinished()
我们看控制台打印:

MainActivity: onChanged: 任务完成了

添加约束

假如我们想要给任务添加约束条件,比如在有网的情况下才执行任务。
我们收需要创建Constraints对象 并设置网络条件为连接状态

   Constraints myConstraints = new Constraints.Builder()

                .setRequiredNetworkType(NetworkType.CONNECTED)
                // Many other constraints are available, see the
                // Constraints.Builder reference
                .build();

在创建WorkRequest对象的时候 传入Constraints对象

     //把 MyWorker 包装成 OneTimeWorkRequest
 OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MyWorker.class)
.setConstraints(myConstraints).build();

我们做一个测试分别在有网和没网的之后启动任务。
我们启动应用程序之后,关闭网络,任务不会执行,再打开网络的时候,任务执行了。

取消任务

加入到任务队列的任务还可以进行取消,通过任务ID取消某个具体的任务

WorkManager.getInstance().cancelWorkById(request.getId());

六、高级功能

WorkManager Api还提供了一些高级功能

1、重复执行任务

以上的例子我们能对图片不止一次压缩,有可能多次压缩,定时压缩。这个时候我们用到的是PeriodicWorkRequest这个类,和上面一样 我们把任务封装成PeriodicWorkRequest,然后我们设置每隔12个小时压缩一次。然后我们在通过WorkManager把PeriodicWorkRequest加入到任务队列即可。

        //把 MyWorker 包装成 PeriodicWorkRequest
        PeriodicWorkRequest request = new PeriodicWorkRequest.Builder(MyWorker.class,12,TimeUnit.HOURS).
setConstraints(myConstraints).build();

    WorkManager.getInstance().enqueue(request);

2、链式任务

有时候我们需要按照顺序执行一串任务,先执行某一个再到某一个,我们可以进行如下操作。创建三个任务A B C,按照顺序执行。

OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(AWorker.class)
.setConstraints(myConstraints).build();
        OneTimeWorkRequest requestB = new OneTimeWorkRequest.Builder(BWorker.class)
.setConstraints(myConstraints).build();
        OneTimeWorkRequest requestC = new OneTimeWorkRequest.Builder(CWorker.class)
.setConstraints(myConstraints).build();
        WorkManager.getInstance().beginWith(requestA).then(requestB).then(requestC).enqueue();

我们也可以一次性执行多个。

        WorkManager.getInstance().beginWith(requestA,requestB).then(requestB,requestC).then(requestC).enqueue();

3、复杂的链式任务

我们可以通过 WorkContinuation.combine()建立复杂的任务链
比如我们想完成如下图的任务链:


Android Jetpack——WorkManager_第1张图片
image.png
   OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(AWorker.class).setConstraints(myConstraints).build();
        OneTimeWorkRequest requestB = new OneTimeWorkRequest.Builder(BWorker.class).setConstraints(myConstraints).build();
        OneTimeWorkRequest requestC = new OneTimeWorkRequest.Builder(CWorker.class).setConstraints(myConstraints).build();
        OneTimeWorkRequest requestD = new OneTimeWorkRequest.Builder(DWorker.class).setConstraints(myConstraints).build();
        OneTimeWorkRequest requestE = new OneTimeWorkRequest.Builder(EWorker.class).setConstraints(myConstraints).build();

        WorkContinuation c1 =  WorkManager.getInstance().beginWith(requestA).then(requestC);
        WorkContinuation c2 =   WorkManager.getInstance().beginWith(requestB).then(requestD);
        WorkContinuation c  =WorkContinuation.combine(c1,c2).then(requestE);
        c.enqueue();

4、唯一的工作序列

我们可以通过beginUniqueWork方法执行唯一序列的工作任务。该方法有三个参数:uniqueWorkName唯一工作序列名称,work需要执行的任务,existingWorkPolicy代表当有重复的任务需要做什么操作。这个参数有三个枚举:
1、REPLACE: 用新任务来取代已经存在的任务
2、KEEP: 保留已经存在的任务. 忽视新任务
3、APPEND: 新任务入列. 新旧任务都存在于队列中.

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

具体使用方式:

 WorkManager.getInstance().beginUniqueWork("a",ExistingWorkPolicy.KEEP,requestA);

5、设置标签

我们之前是根据任务的ID获取任务,我们也可以给任务设置标签Tag,通过Tag来获取任务。

 OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(AWorker.class)
.addTag("tag").setConstraints(myConstraints).build();

WorkManager.getInstance().getStatusesByTag("tag");

6、输入参数和返回值

我们可以给任务传递参数,也可以获取任务的返回结果。

给任务传递参数

1、传递参数
我们首先需要创建需要传递的数据,key value的形式。然后再创建WorkRequest对象的时候进行设置。

    
Data data = new Data.Builder().putInt("a",1).putInt("b",2).build();
OneTimeWorkRequest requestE = new OneTimeWorkRequest.Builder(EWorker.class).setInputData(data).build();
WorkManager.getInstance().enqueue(requestE);

2、在任务中获取传递来的参数

package com.example.administrator.workmanager;

import android.support.annotation.NonNull;
import android.util.Log;

import androidx.work.Worker;

public class EWorker extends Worker {
    private static final String TAG = "MyWorker";

    @NonNull
    @Override
    public WorkerResult doWork() {
        //压缩方法
        compress();
        int a = getInputData().getInt("a",0);
        int b = getInputData().getInt("b",0);
        Log.e(TAG, "doWork: "+a+":"+b );
        return WorkerResult.SUCCESS;
    }

    /**
     * 压缩图片任务
     */
    private void compress() {
        Log.e(TAG, "compress: 对图片进行压缩" );

    }
}

我们看打印结果,确实是我们传递过来的参数。

03-20 17:37:00.053 25227-25258/com.example.administrator.workmanager E/MyWorker: compress: 对图片进行压缩
03-20 17:37:00.053 25227-25258/com.example.administrator.workmanager E/MyWorker: doWork: 1:2
获取任务的返回结果

1、获取任务中的返回值,即获取任务结果
我们在任务中创建数据,并调用setOutputData方法,传递出来

package com.example.administrator.workmanager;

import android.support.annotation.NonNull;
import android.util.Log;

import androidx.work.Data;
import androidx.work.Worker;

public class EWorker extends Worker {
    private static final String TAG = "MyWorker";

    @NonNull
    @Override
    public WorkerResult doWork() {
        //压缩方法
        compress();
        Data data = new Data.Builder().putInt("a",1).putInt("b",2).build();
        setOutputData(data);
        return WorkerResult.SUCCESS;
    }

    /**
     * 压缩图片任务
     */
    private void compress() {
        Log.e(TAG, "compress: 对图片进行压缩" );

    }
}

2、获取任务的返回结果

  WorkManager.getInstance().getStatusById(requestE.getId()).observe(this, new Observer() {
            @Override
            public void onChanged(@Nullable WorkStatus workStatus) {
                    if(workStatus!=null){
                        int result  = workStatus.getOutputData().getInt("a",0);
                        Log.e(TAG, "onChanged: "+result );
                    }
            }
        })

我们看打印结果:

E/MainActivity: onChanged: 1

这样我们就得到了任务中的数据1

七、小结

通过以上学习我们知道
1、WorkManager可以作为执行后台任务的组建,并且不依赖应用的是否退出
2、可以执行多个任务,并设计任务链
3、可以添加约束在指定的环境,时间执行任务。
4、可以向任务传递参数,也可以获取任务的返回值

你可能感兴趣的:(Android Jetpack——WorkManager)