Android Jetpack WorkManager初级认识

1,162 阅读6分钟

本篇文章已授权微信公众号guolin_blog(郭霖)独家发布

Workmanager简介

WorkManager是用于使可延期工作入队的库,根据设备API级别和应用程序状态等因素选择适当的方式来运行任务。如果WorkManager在应用程序运行时执行您的任务之一,WorkManager可以在您应用程序进程的新线程中运行您的任务。如果您的应用程序未运行,WorkManager会选择一种合适的方式来安排后台任务 - 具体取决于设备API级别和包含的依赖项,WorkManager可能会使用 JobSchedulerFirebase JobDispatcherAlarmManager

该库可保证在Constraints满足要求后的某个时间执行。WorkManager允许观察工作状态以及创建复杂工作链的能力。

WorkManager支持两种类型的工作:OneTimeWorkRequest和 PeriodicWorkRequest。您可以使用WorkManager排队请求,如下所示:

基本相关类(不全面):

  • Workerextend抽象的Worker类,在实现的方法执行逻辑操作
  • WorkRequest.Builder:用于构建WorkRequest对象;
  • WorkRequest:单一任务的请求 系统已经实现了两个可以使用的子类
  1. OneTimeWorkRequest:只执行一次工作。
  2. PeriodicWorkRequest :重复性执行工作。这项工作执行多次,直到取消。下一个执行期间会发生间隔;

请注意, 执行可能会被推迟,因为受到Constraints下设置的电池优化,, 息屏,网络 等模式影响。

  • Constraints: 默认情况下,WorkRequests可以立即运行。通过添加Constraints可以对指定对任务运行的状态下进行约束(比如没网络,息屏,电池优化等);使用Constraints.Builder构建Constraints对象 ,并传递给WorkRequest.Builder
  • Data : 用于构建一组持续化可供键值对。

WorkManager 使用

添加依赖:

    //增加workManager管理后台任务
    implementation "android.arch.work:work-runtime:1.0.1"
    implementation "android.arch.work:work-firebase:1.0.0-alpha11"

##使用方式

  • 简单使用方式

1 实现Worker : 自定义MainWorker,重写doWork,并返回结果

class MainWorker(context: Context,workerParameters: WorkerParameters) :Worker(context,workerParameters){
    override fun doWork(): Result {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
}

2 调用 由OneTimeWorkRequest.Builder().build()创建对象WorkRequest,然后将任务放入的WorkManager管理的WorkRequest队列(本次文章中都以OneTimeWorkRequest来写例子 )

val baseWork = OneTimeWorkRequest.Builder(MainWorker::class.java).build()
WorkManager.getInstance().enqueue(baseWork)

这样子简单的使用方式就可以了~~~~

但是如果你要监听数据信息然后获取数据进行逻辑操作呢(数据交互)?

可以看一下下面的使用···:

  • 使用方式2 :监听数据方式

1实现work 这里面inputData.getString("LiveDataTest")是从调用的时候传入进来的数据,ruturn的时候使用Result.success(data) 'data'的数据类型的Data返回的数据可以再output中的键值对获取到

class BtnTwoWorker(context: Context, workerParameters: WorkerParameters) :Worker(context,workerParameters){

    override fun doWork(): Result {
        Log.i("MainWorker","点击${inputData.getString("LiveDataTest")}")
        val data = Data.Builder().putString("LiveDataTest","LiveDataTest修改后的信息---").build()
        return  Result.success(data)
    }
}

2 使用

代码中的第一步构建的就是 步骤一中的LiveDataTest的数据,然后构建work设置setInputData(data ) ,通过WorkManager.getInstance().getWorkInfoByIdLiveData(liveDataWork.id)获取LiveData<WorkInfo>并进行监听数据,在监听数据中可以获取到outputdata的键值对进行逻辑处理。 最后将构建的liveDataWork放入WorkManager中的队列等待运行 WorkManager.getInstance().enqueue(liveDataWork)

 //1 构建 Data数据
 var data = Data.Builder().putString("LiveDataTest","有生命的数据监听··").build()

//2 构建work
  val liveDataWork = OneTimeWorkRequest.Builder(BtnTwoWorker::class.java).setInputData(data).build()

//3 通过work的id获取Status
val status =  WorkManager.getInstance().getWorkInfoByIdLiveData(liveDataWork.id)

//4 监听状态以及数据的变化
//注意一点:这里的监听的时候 状态改变Observer就会跑一遍~~
   status.observe(this, Observer {
   if(it.state == WorkInfo.State.SUCCEEDED){
   mViewModel.data.value = it.outputData.getString("LiveDataTest")
   Log.i("WorkerActivity", it.outputData.getString("LiveDataTest")?:"空")
   }
   Log.i("WorkerActivity",it.state.name)
 Log.i("WorkerActivity","${it.state.isFinished}")
            })
//再放入到WorkManager的队列中
 WorkManager.getInstance().enqueue(liveDataWork)

/////////////////////////////////////////////////////
2019-10-14 16:54:33.951 15770-15770/testview.zhen.com.myapplication I/WorkerActivity: ENQUEUED
2019-10-14 16:54:33.951 15770-15770/testview.zhen.com.myapplication I/WorkerActivity: false
2019-10-14 16:54:33.972 15770-15849/testview.zhen.com.myapplication I/MainWorker: 点击有生命的数据监听··
2019-10-14 16:54:33.975 15770-15800/testview.zhen.com.myapplication I/WM-WorkerWrapper: Worker result SUCCESS for Work [ id=fa81ea63-6c23-4d40-a72f-7f0eea3baf91, tags={ testview.zhen.com.myapplication.jetpack.BtnTwoWorker } ]
2019-10-14 16:54:34.000 15770-15770/testview.zhen.com.myapplication I/WorkerActivity: LiveDataTest修改后的信息---
2019-10-14 16:54:34.000 15770-15770/testview.zhen.com.myapplication I/WorkerActivity: SUCCEEDED
2019-10-14 16:54:34.000 15770-15770/testview.zhen.com.myapplication I/WorkerActivity: true

从上面执行过程中看出,回调了Work的两个运行状态RUNNING、SUCCESSESD 至于it.state.isFinished这里面的实现为:

        /**
         * Returns {@code true} if this State is considered finished.
         *
         * @return {@code true} for {@link #SUCCEEDED}, {@link #FAILED}, and * {@link #CANCELLED}
         *         states
         */
        public boolean isFinished() {
            return (this == SUCCEEDED || this == FAILED || this == CANCELLED);
        }

当任务 成功/失败/取消 都是结束~

  • 增加任务约束的使用

1 使用Constraints.Builder()创建并配置Constraints对象 里面的设置信息在API中皆有体现 如下图:

image.png

  • setRequiresBatteryNotLow:是否为低电量时运行

  • setRequiredNetworkType:网络连接设置

  • setRequiresCharging:是否在充电情况下运行

  • setRequiresDeviceIdle:设备是否为空闲

  • setRequiresStorageNotLow:设备可用存储是否不低于临界阈值 以上方法默认值都是 false

  • 使用方式3 :增加情况限制 Constraints

class DelayWorker(context: Context, workerParameters: WorkerParameters) :Worker(context,workerParameters){

    override fun doWork(): Result {
        Log.i("MainWorker","点击${inputData.getString("delayWork")} --所在线程 ${Thread.currentThread().name}")
        val time = inputData.getString("time")

        val data = Data.Builder().putString("LiveDataTest","LiveDataTest修改后的信息---").build()
        return  Result.success(data)

    }


}
           //1 增加约束条件
            val constrains = Constraints.Builder()
                    .setRequiredNetworkType(NetworkType.CONNECTED)
                    .setRequiresBatteryNotLow(true)
                    .build()
            //2 构建 Data数据
            var data = Data.Builder().putString("delayWork","延时操作~~~").putInt("time",10).build()

            //3 构建workRequest
            val workConstrains =  OneTimeWorkRequest.Builder(DelayWorker::class.java)
                    .setConstraints(constrains)
                    .setInputData(data)
                    .build()
            //4 获取Status监听数据
            val status = WorkManager.getInstance().getWorkInfoByIdLiveData(workConstrains.id)
            status.observe(this, Observer {
                Log.i("WorkerActivity",it?.state?.name)
                if (it?.state!!.isFinished) {
                    Log.e("TestWorker", "Finish")
                }
            })
            //5 添加到equque
            WorkManager.getInstance().enqueue(workConstrains)

////////////////////////////////////////////////////////////////////////////////////
2019-10-14 17:57:28.140 25296-25296/testview.zhen.com.myapplication I/WorkerActivity: ENQUEUED            【------没有连接网络的时候先等待】
2019-10-14 17:57:36.609 25296-25296/testview.zhen.com.myapplication I/WorkerActivity: RUNNING                 【------有网络的时候执行】
2019-10-14 17:57:36.610 25296-25469/testview.zhen.com.myapplication I/MainWorker: 点击延时操作~~~ --所在线程 pool-1-thread-1
2019-10-14 17:57:36.613 25296-25325/testview.zhen.com.myapplication I/WM-WorkerWrapper: Worker result SUCCESS for Work [ id=fc71f417-8289-4f8c-a489-4d8699960f3d, tags={ testview.zhen.com.myapplication.jetpack.DelayWorker } ]
2019-10-14 17:57:36.637 25296-25296/testview.zhen.com.myapplication I/WorkerActivity: SUCCEEDED

其它一些使用

  • #####取消任务
    通过获取WorkRequestID或者TAG 获取到相应的WorkRequest从而
//取消任务 方式1:
WorkManager.getInstance().cancelWorkById(workConstrains.id)
//取消任务 方式2:   (这个方式我取消了但是不知道为什么任务都不会停止) 
//这里传入的参数  true 如果线程执行, 任务应该被打断  false,允许在进行任务, 完成;
//WorkManager.getInstance().getWorkInfoById(workConstrains.id).cancel(true) 


/** 通过在WorkRequestBuilder 设置 TAG 然后获取TAG进行取消任务 **/
 OneTimeWorkRequest.Builder(BtnTwoWorker::class.java)
                    .addTag("TAG")
                    .setInputData(data)
                    .build()

//取消任务 方式2:
 WorkManager.getInstance().getStatusesByTag("TAG")
//取消任务 具有特定标记的所有任务 **TAG可以相同**
 WorkManager.getInstance().cancelAllWorkByTag("TAG") 

##多任务高级进阶使用(参考官网)

顺序执行

WorkRequest request1 = new OneTimeWorkRequest.Builder(FooWorker.class).build();
 WorkRequest request2 = new OneTimeWorkRequest.Builder(BarWorker.class).build();
 WorkRequest request3 = new OneTimeWorkRequest.Builder(BazWorker.class).build();
 workManager.beginWith(request1, request2).then(request3).enqueue();

每次调用beginWith(OneTimeWorkRequest)beginWith(List)返回一个WorkContinuation,您可以在上调用 WorkContinuation.then(OneTimeWorkRequest)WorkContinuation.then(List)链接进一步的工作。这允许创建复杂的工作链。例如,要创建这样的链:

image.png

 WorkContinuation continuation = workManager.beginWith(A);
 continuation.then(B).then(D, E).enqueue();  // A is implicitly enqueued here
 continuation.then(C).enqueue();

Note: 完成所有先决条件后,工作才有资格执行。如果其任何先决条件失败或被取消,则该工作将永远不会进行。

  • 任务唯一性 很多情况下,我们希望在任务队列里,同一个任务只存在一个,避免任务的重复执行,这时候可以用到 beginUniqueWork 这个方法:
WorkManager.getInstance()
        .beginUniqueWork("onlyOne", ExistingWorkPolicy.KEEP, work)
        .enqueue()

三个参数 唯一名字、 冲突的处理方式、OneTimeWorkRequest的任务。 冲突的方式enum类型:

REPLACE :如果有现有的等待(未完成)使用相同的惟一名称,取消和删除它。然后插入现有的任务。
KEEP:如果有现有的等待(未完成)使用相同的惟一名称,则什么也不做。 否则,插入现有的任务。
APPEND:如果有现有的等待(未完成)使用相同的惟一名称附加的, 新指定的工作作为一个孩子的所有的叶子工作序列。否则,插入现有的任务开始一个新的序列