Android Jetpack——WorkManager

作者: 大虾啊啊啊 | 来源:发表于2019-03-20 17:52 被阅读4次

    一、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<WorkStatus>() {
                @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()建立复杂的任务链
    比如我们想完成如下图的任务链:


    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<WorkStatus>() {
                @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

        本文链接:https://www.haomeiwen.com/subject/zaubmqtx.html