Android Jetpackandroid

WorkManager 最全攻略

2020-04-29  本文已影响0人  sunjiandev

1. 介绍

关于workmanager 的介绍 官网上是这么说的: 使用 WorkManager API 可以轻松地调度即使在应用退出或设备重启时仍应运行的可延迟异步任务。 重点是后面的几个字,运行可延迟的异步任务, 在退出或者重启的时候,我们在之前要实现这种任务可能需要 BroadcastReceiver 或者 AlarmManager,现在的话可以使用WorkManager,这个框架最高兼容至Android api 14

2. 优点

3. 使用

3.1 为项目添加依赖

//根据项目需要自行添加依赖,不需要全部添加
    dependencies {
      def work_version = "2.3.1"

        // (Java only)
        implementation "androidx.work:work-runtime:$work_version"//java 语言选这个

        // Kotlin + coroutines
        implementation "androidx.work:work-runtime-ktx:$work_version"//kotlin 选这个

        // optional - RxJava2 support
        implementation "androidx.work:work-rxjava2:$work_version"//配合rxjava2 使用

      }
    

3.2 创建一个后台任务

代理示例如下(以上传图片为例):

class UploadPicWork(
    private val context: Context,
    private val workerParameters: WorkerParameters
) :
    Worker(context, workerParameters) {
    override fun doWork(): Result {

        uploadPic()//具体上传图片的逻辑

        return Result.success()
    }
}

3.3 创建一个workrequest

//此处的 UploadPicWork 就是之前创建的任务
val uploadPicWork = OneTimeWorkRequestBuilder<UploadPicWork>()
                .setConstraints(triggerContentMaxDelay).build()

3.4 执行任务

//此处的 uploadPicWork 就是前一步创建的 workrequest
  WorkManager.getInstance(myContext).enqueue(uploadPicWork)

到这里一个简单的任务就可以执行了,但往往我们在开发的过程中,可能满足不了我们的需求,再继续往下看!

4. 创建一个"复杂的任务"

4.1 创建任务执行的约束条件


//注意 以下条件都是 && 的关系

val triggerContentMaxDelay =
                Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)//网络链接的时候使用,避免各种网络判断,省时省力
                .setRequiresDeviceIdle(false)//是否在设备空闲的时候执行
                .setRequiresBatteryNotLow(true)//是否在低电量的时候执行
                .setRequiresStorageNotLow(true)//是否在内存不足的时候执行
                .setRequiresCharging(true)//是否时充电的时候执行
                .setTriggerContentMaxDelay(1000 * 1, TimeUnit.MILLISECONDS)//延迟执行
                .build()
                

//目前就提供这几种约束条件,大家可以在使用的过程中酌情添加

4.2 为任务添加约束条件


val uploadPicWork =
                OneTimeWorkRequestBuilder<UploadPicWork>()
                    .setConstraints(triggerContentMaxDelay)//约束条件
                    .build()

 WorkManager.getInstance(myContext).enqueue(uploadPicWork)//执行
 

4.3 为worker 传递参数

这个可能比较重要,我们一般在执行任务的时候需要把参数传递到Worker中,可以采用构造或者 写一个set方法给传递进去,但是这种方式在这可能不适用


//可以采用这种方式传递参数
   val UploadPicWork =
                OneTimeWorkRequestBuilder<UploadPicWork>()
                    //此处set input data 需要的参数 是一个Data对象,注意只可以添加一次,如果有多个参数需要传递,可以封装成一个data 数据类
                    .setInputData(workDataOf("params_tag" to "params"))
                    .setConstraints(triggerContentMaxDelay).build()

    ...
    

/**
 * Converts a list of pairs to a [Data] object.
 *
 * If multiple pairs have the same key, the resulting map will contain the value
 * from the last of those pairs.
 *
 * Entries of the map are iterated in the order they were specified.
 */
inline fun workDataOf(vararg pairs: Pair<String, Any?>): Data {
    val dataBuilder = Data.Builder()
    for (pair in pairs) {
        dataBuilder.put(pair.first, pair.second)
    }
    return dataBuilder.build()
}

可以看看这个 workDataOf 函数,就是将一个 Pair对象转成一个Data对象,将
pair.first 作为 key ,pair.second 作为 value 构建了一个Data 对象. 有的同学可能对 "params_tag" to "params" 这种写法比较差异,解释一下,这是构造了一个 Pair 对象,
不了解 Pair 这个类的同学,可以看下Pair,参数的类可以是基本数据类型,也可以引用数据类型

4.4 获取参数


class UploadPicWork(
    private val context: Context,
    private val workerParameters: WorkerParameters
) :
    Worker(context, workerParameters) {
    override fun doWork(): Result {

       val params = inputData.getString("params_tag")//获取传递的参数

        uploadPic()//上传图片

        return Result.success()
    }
}

inputDataWorker 类 父类的一个函数,在Java 中 可以 类似 this.getInputData() 返回的是一个 Data 对象,就可以获取传递的参数了.

5. Worker 的状态

在之前的创健的过程中,在 doWork 函数中,我们返回的 Result.success(); 我们默认 ,任务 uploadPic 函数顺利的执行完成了,所以返回了 success 状态,但是在实际开发过程中 可以能因为各种各样的问题会导致 失败,这时候就不能返回success了,类似这样:



class UploadPicWork(
    private val context: Context,
    private val workerParameters: WorkerParameters
) :
    Worker(context, workerParameters) {
    override fun doWork(): Result {

       val params = inputData.getString("params_tag")//获取传递的参数

        try {
            uploadPic()//上传图片
        } catch (e: Exception) {
            return Result.failure(Data())//执行失败了
        }

        return Result.success()
    }
}

Result.failure(Data()),这个函数可以什么都不传,但是如果关注失败的原因的话,可以把封装一个Data对象,传递出去!!,至于怎么观察任务的执行结果,以及拿到执行失败传递的参数,后面会讲!

5.1 Worker 的各种状态说明

在Worker 生命周期内,会经历不同的 State

6. 观察Worker 的状态(需要搭配 LiveData组件使用)

将工作加入队列后,可以通过 WorkManager 检查其状态。相关信息在 WorkInfo 对象中获取,包括 Workeridtag、当前 State 和返回数据。

6.1 获取 WorkInfo

 WorkManager.getInstance(this)
                .getWorkInfoByIdLiveData(UploadPicWork.id)// 通过id 获取
                .observe(this, Observer { //it:WorkInfo
                    it?.apply {
                        when (this.state) {
                            WorkInfo.State.BLOCKED -> println("BLOCKED")
                            WorkInfo.State.CANCELLED -> println("CANCELLED")
                            WorkInfo.State.RUNNING -> println("RUNNING")
                            WorkInfo.State.ENQUEUED -> println("ENQUEUED")
                            WorkInfo.State.FAILED -> println("FAILED")
                            WorkInfo.State.SUCCEEDED -> println("SUCCEEDED")
                            else -> println("else status ${this.state}")
                        }
                    }

                })

//要通过 tag 获取,则需要先设置 tag
val UploadPicWork =
                OneTimeWorkRequestBuilder<UploadPicWork>()
                    .setInputData(workDataOf("params_tag" to "params"))//传递参数
                    .setConstraints(triggerContentMaxDelay)//设置约束条件
                    .addTag("tag")//设置tag
                    .build()

//获取 workInfo

WorkManager.getInstance(this)
                .getWorkInfosByTagLiveData("tag")
                .observe(this, Observer {it:List<WorkInfo>//此处返回的是一个集合,作为示例代码,默认只取 0 index
                    it?.apply {
                        when (this[0].state) {
                            WorkInfo.State.BLOCKED -> println("BLOCKED")
                            WorkInfo.State.CANCELLED -> println("CANCELLED")
                            WorkInfo.State.RUNNING -> println("RUNNING")
                            WorkInfo.State.ENQUEUED -> println("ENQUEUED")
                            WorkInfo.State.FAILED -> println("FAILED")
                            WorkInfo.State.SUCCEEDED -> println("SUCCEEDED")
                            else -> println("else status ${this[0]}")
                        }
                    }

                })

WorkManager.getInstance(this)
                .getWorkInfosForUniqueWorkLiveData("UploadPicWork")//唯一工作名称 
                .observe(this, Observer {it:List<WorkInfo> //此处返回的是一个集合,作为示例代码,默认只取 0

                    it?.apply {
                        when (this[0].state) {
                            WorkInfo.State.BLOCKED -> println("BLOCKED")
                            WorkInfo.State.CANCELLED -> println("CANCELLED")
                            WorkInfo.State.RUNNING -> println("RUNNING")
                            WorkInfo.State.ENQUEUED -> println("ENQUEUED")
                            WorkInfo.State.FAILED -> println("FAILED")
                            WorkInfo.State.SUCCEEDED -> println("SUCCEEDED")
                            else -> println("else status ${this[0]}")
                        }
                    }

                })

注意如采用这种方式获取 workinfo ,在执行 worker 的时候与之前不一样,需要采用 WorkManager.enqueueUniqueWork(String, ExistingWorkPolicy, OneTimeWorkRequest)WorkManager.enqueueUniquePeriodicWork(String, ExistingPeriodicWorkPolicy, PeriodicWorkRequest) 来执行

//全部代码如下


//创建约束条件
val triggerContentMaxDelay =
                Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED)
//                    .setRequiresDeviceIdle(false)
                    .setRequiresBatteryNotLow(true)
                    .setRequiresStorageNotLow(true)
                    .setRequiresCharging(true)
                    .setTriggerContentMaxDelay(1000 * 1, TimeUnit.MILLISECONDS)
                    .build()

// 创建workrequest
            val UploadPicWork =
                OneTimeWorkRequestBuilder<UploadPicWork>()
                    .setInputData(workDataOf("params_tag" to "params"))
                    .setConstraints(triggerContentMaxDelay)
                    .build()

    //注意!!!,此处区别与之前的 WorkManager.getInstance(this).enqueue(UploadPicWork)
    
    // "UploadPicWork" 需要与下面代码 getWorkInfosForUniqueWorkLiveData("UploadPicWork") 中字符串对应
    // ExistingWorkPolicy.APPEND 一个枚举值,worker 执行的策略,想要了解的同学,可以看下面的链接
    WorkManager.getInstance(this).enqueueUniqueWork("UploadPicWork",ExistingWorkPolicy.APPEND,UploadPicWork)

            WorkManager.getInstance(this)
                .getWorkInfosForUniqueWorkLiveData("UploadPicWork")
                .observe(this, Observer {

                    it?.apply {
                        when (this[0].state) {
                            WorkInfo.State.BLOCKED -> println("BLOCKED")
                            WorkInfo.State.CANCELLED -> println("CANCELLED")
                            WorkInfo.State.RUNNING -> println("RUNNING")
                            WorkInfo.State.ENQUEUED -> println("ENQUEUED")
                            WorkInfo.State.FAILED -> println("FAILED")
                            WorkInfo.State.SUCCEEDED -> println("SUCCEEDED")
                            else -> println("else status ${this[0]}")
                        }
                    }



ExistingWorkPolicy

7. 多个Worker 的顺序执行

在我们实际开发中可能遇到如下场景.一个任务可能依赖与其他的任务,并且每个任务之间有先后顺序,以前面简介中,图片上传为例:上传图片之前 需要 先压缩,压缩之前需要先剪裁, 流程如下 滤镜--> 压缩 --> 上传图片,对于这部分内容,官网上介绍的已经足够清清楚了! 链接工作

    WorkManager.getInstance(myContext)
        // Candidates to run in parallel
        //滤镜1 滤镜2 滤镜3 ...
        .beginWith(listOf(filter1, filter2, filter3))
        // Dependent work (only runs after all previous work in chain)
        //压缩
        .then(compress)
        //上传
        .then(upload)
        // Don't forget to enqueue()
        .enqueue()//执行

8. 执行重复任务

很好理解,就是在给定的时间间隔内定期执行任务,比如说 每个一个小时,上报位置信息,每个3个小时备份一个日志等等... 代码示例如下:


val triggerContentMaxDelay =
                Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED)
//                    .setRequiresDeviceIdle(false)
                    .setRequiresBatteryNotLow(true)
                    .setRequiresStorageNotLow(true)
                    .setRequiresCharging(true)
                    .setTriggerContentMaxDelay(1000 * 1, TimeUnit.MILLISECONDS)
                    .build()

//            val UploadPicWork =
//                OneTimeWorkRequestBuilder<UploadPicWork>()
//                    .setInputData(workDataOf("params_tag" to "params"))
//                    .setConstraints(triggerContentMaxDelay)
//                    .addTag("tag")
//                    .build()
//

            val build = PeriodicWorkRequestBuilder<UploadPicWork>(
                1000 * 60 *15,
                TimeUnit.MICROSECONDS
            ).setConstraints(triggerContentMaxDelay).build()

            WorkManager.getInstance(this).enqueue(build)

注意!!!这个时间间隔不可低于15分钟

9.任务执行失败重试

这个场景在实际开发中也经常遇到,比如在任务执行的过程中可能由于一些别的原因导致任务执行失败,但是我们希望过一段时间可以重试 代码示例如下:


//约束条件
  val triggerContentMaxDelay =
                Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED)
//                    .setRequiresDeviceIdle(false)
                    .setRequiresBatteryNotLow(true)
                    .setRequiresStorageNotLow(true)
                    .setRequiresCharging(true)
                    .setTriggerContentMaxDelay(1000 * 1, TimeUnit.MILLISECONDS)
                    .build()

            val UploadPicWork =
                OneTimeWorkRequestBuilder<UploadPicWork>()
                    .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10 * 1000, TimeUnit.MICROSECONDS)//10s 后失败重试
                    .setInputData(workDataOf("params_tag" to "params"))
                    .setConstraints(triggerContentMaxDelay)
                    .addTag("tag")
                    .build()
                    
            WorkManager.getInstance(this).enqueue(UploadPicWork)


...

class UploadPicWork(
    private val context: Context,
    private val workerParameters: WorkerParameters
) :
    Worker(context, workerParameters) {

    private var count: Int = 1

    override fun doWork(): Result {
        
        uploadPic()

        return Result.retry()//失败重试状态
    }

    private fun uploadPic() {
        SystemClock.sleep(1000 * 3)//模拟图片上传
    }
}


上一篇下一篇

猜你喜欢

热点阅读