Jetpack 学习笔记——WorkManager(一)

2,223 阅读6分钟

简述

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

按照我的理解,WorkManager就是用来管理我们异步任务的管理器,通过这个管理器我们可以更轻松管理我们项目中的异步任务。

作为android开发者来说,我们在没有WorkManager之前, 我们也可以使用 JobScheduler, AlarmManger 、 AsyncTask 等管理后台任务类进行异步任务处理。那为啥官方还要推出WorkManager呢?

WorkManager优势

  1. 兼容性:

    最高向后兼容到 API 14 ,WorkManager会根据运行环境自动选择不同的管理类,

    在运行 API 23 及以上级别的设备上使用 JobScheduler ;

    在运行 API 14-22 的设备上结合使用 BroadcastReceiver 和 AlarmManager ;

  2. 可延迟性:

    WorkManager 旨在用于可延迟运行(即不需要立即运行);

  3. 可靠性:

    WorkManager 可以在应用退出或设备重启时依旧可靠执行任务。(这个在真机上测试时并没有成功,不过在虚拟机上可以实现)

  4. 可控性:

    我们可以对任务指定在特定情况下执行,可以通过 WorkRequest 添加网络可用性或充电状态等工作约束 ,我们也可以简便的进行取消操作。

接入指南

添加 WorkManager 的依赖项,您必须将 Google Maven 代码库添加到项目中:

在应用或模块的 build.gradle 文件中添加所需工件的依赖项:

    dependencies {
      def work_version = "2.3.4"

        // (Java only)
        implementation "androidx.work:work-runtime:$work_version"

        // Kotlin + coroutines
        implementation "androidx.work:work-runtime-ktx:$work_version"

        // optional - RxJava2 support
        implementation "androidx.work:work-rxjava2:$work_version"

        // optional - GCMNetworkManager support
        implementation "androidx.work:work-gcm:$work_version"

        // optional - Test helpers
        androidTestImplementation "androidx.work:work-testing:$work_version"
      }
    

使用入门

1.创建后台任务( Worker

Worker 任务的执行者,是一个抽象类,用于指定需要执行的具体任务,需要实现doWork() 这一个方法,它是执行在一个单独的后台线程里的。

例如,要创建上传图像的 Worker,您可以执行以下操作:

    public class UploadWorker extends Worker {

        public UploadWorker(
            @NonNull Context context,
            @NonNull WorkerParameters params) {
            super(context, params);
        }

        @Override
        public Result doWork() {
          // Do the work here--in this case, upload the images.

          uploadImages()

          // Indicate whether the task finished successfully with the Result
          return Result.success()
        }
    }
    

doWork()函数的返回值:

  • Worker.Result.SUCCESS:任务执行成功。
  • Worker.Result.FAILURE:任务执行失败。
  • Worker.Result.RETRY:任务需要重新执行,如果出现这个返回结果,就需要与WorkRequest.Builder中的setBackoffCriteria()函数一起使用。

2.配置运行任务的方式和时间

WorkRequest 代表一个单独的任务,对Worker任务进行包装,一个WorkRequest对应一个Worker类

WorkRequest是一个抽象类,具体要使用两个子类:OneTimeWorkRequest(任务只执行一遍)、PeriodicWorkRequest(任务周期性的执行)。

我们可以使用 WorkRequest.Builder 构建出WorkRequest ,比如为 UploadWorker 构建 WorkRequest 最简单的示例为:

 OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadWorker.class)
            .build()
    
    

我们前面提到了我们可以通过WorkRequest 添加工作约束,这时候我们需要借助 Constraints ,这个我会在后面说明。

3. 将任务提交给系统

定义 WorkRequest 之后,我们现在现在可以通过 WorkManager 使用 enqueue() 方法来调度它。 例如:

    WorkManager.getInstance(myContext).enqueue(uploadWorkRequest);
   

小结:简单的三部曲就实现了一个异步任务的执行。当然这是最简单的一个异步任务,这肯定是不能满足我们的,下面将介绍更多的api及更高阶的用法。

定义WorkRequest

上述内容只是简单的发起一个异步工作,当然在实际开发中,我们会遇到很多场景,这时候就需要我们定义工作请求。

1.约束条件

我们可以构建出Constraints对象,进行工作请求的约束, 例如,您可以指定工作应仅在设备空闲且接通电源时运行,或者我们也可以约束在低电量时不执行等。如下列添加条件

  //构建限制条件 为了方便测试这里只加充电
        Constraints constraints = new Constraints.Builder()
               // .setRequiredNetworkType(NetworkType.CONNECTED) //网络状态
                //.setRequiresBatteryNotLow(true) //不是低电量
                //.setRequiresDeviceIdle(true)//设备待机空闲
                //.setRequiresStorageNotLow(true)//内存不紧张
                .setRequiresCharging(true) //充电
                .build();
    request=new OneTimeWorkRequest.Builder(MyWorker.class)
               // .setInputData(inputData)
                .setConstraints(constraints)//设置条件
                .build();

有关所支持约束的完整列表,请参阅 Constraints.Builder 参考文档。 下面罗列一些api

/**
 * 是否在充电状态下执行任务
 */
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);

2.传参

您的任务可能需要数据以输入参数的形式传入,或者将数据返回为结果。 我们可以构建Data 进行数据传输。

    Data imageData = new Data.Builder()
                    .putString(Constants.KEY_IMAGE_URI, imageUriString)
                    .build();

    OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadWorker.class)
            .setInputData(imageData)
            .build();
    

3.更多API介绍

对于OneTimeWorkRequest,我们还可以进行初始化延迟操作、重启、标记等工作;

1.延迟任务启动;

setInitialDelay(10, TimeUnit.MINUTES)//延迟

2.重试和退避政策

.setBackoffCriteria(BackoffPolicy.LINEAR,                    OneTimeWorkRequest.MIN_BACKOFF_MILLIS,                    
                    TimeUnit.MILLISECONDS)

3.标记任务

addTag()进行任务标记.

 OneTimeWorkRequest cacheCleanupTask =
            new OneTimeWorkRequest.Builder(CacheCleanupWorker.class)
        .setConstraints(constraints)
        .addTag("cleanup")
        .build();

工作管理

工作状态

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

// 获取到LiveData然后监听数据变化
        WorkManager.getInstance().getStatusById(request.getId()).observe(this, new Observer<WorkStatus>() {
            @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"));
                }
            }
        });

工作链

您可以使用 WorkManager 创建工作链并为其排队。工作链用于指定多个关联任务并定义这些任务的运行顺序。当您需要以特定的顺序运行多个任务时,这尤其有用。

链式任务的关键在WorkContinuation,通过WorkContinuation来整理好队列(是顺序执行,还是组合执行)然后入队执行。

WorkContinuation里面常用函数介绍 :

    /**
     * 顺序执行任务,
     * 如果then的参数指定了多个任务那么这些任务的执行顺序是没有规律的,但是一定要等这个then函数参数里面所有任务都执行完了才会去执行下一个then的任务
     * 任务任务返回Worker.WorkerResult.FAILURE整个则整个任务链结束
     */
    public final @NonNull WorkContinuation then(@NonNull OneTimeWorkRequest... work);
    public abstract @NonNull WorkContinuation then(@NonNull List<OneTimeWorkRequest> work);


    /**
     * 这些都是static函数哦,用于组合任务
     */
    public static @NonNull WorkContinuation combine(@NonNull WorkContinuation... continuations);
    public static @NonNull WorkContinuation combine(@NonNull List<WorkContinuation> continuations);
    public static @NonNull WorkContinuation combine(@NonNull OneTimeWorkRequest work,
        @NonNull WorkContinuation... continuations);
    public static @NonNull WorkContinuation combine(@NonNull OneTimeWorkRequest work,
        @NonNull List<WorkContinuation> continuations);

    /**
     * 获取任务链中所有任务的LiveData,用于监听任务链里面任务的结果
     */
    public abstract @NonNull LiveData<List<WorkStatus>> getStatuses();

    /**
     * 任务链中的任务入队,开始执行。
     */
    public abstract void enqueue();

总结

上述内容主要介绍如何简单使用、及一些api的用法介绍等,当然还有一些更高级用法,将在下篇中完善。