WorkManager使用记录

1、介绍WorkManager

系统提供的一个新的任务形式,旨在替代Service,因为Service比较重,且前台感知能力太强(需要发送通知),因此Google推出了WorkManager,并对任务做了一些限制,以更好的适配系统任务调度,起到降低开发成本,降低设备功耗的作用

2、创建一个Task

  1. 创建work

class DemoWork(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
    override fun doWork(): Result {
        // 拿到输入的参数
        val string = inputData.getString("inputData") ?: ""
        Log.d("WorkManagerHelper", string)
        return Result.success() // 成功
        //return Result.failure() // 失败
        //return Result.retry() // 重试
    }

    // 不是一定要实现的方法
    override fun onStopped() {
        super.onStopped()
    }
}

注意:

1、onStopped的回调,并不代表任务执行完毕,而是在用户手动触发了终止,或者由于系统原因,此任务无法再执行时,也会触发这个回调

2、通过Result.success(),failure(),Result.retry()的方式回调任务执行结果

  1. work

val inputData = Data.Builder().putString("inputData", "这是传入的数据").build()
val request = OneTimeWorkRequest.Builder(DemoWork::class.java)
WorkManager.getInstance(context).enqueue(request)

3、创建单次任务

同上

4、创建重复任务

/**
 * 周期性任务,最低时间间隔为15分钟,如果设置的低于15分钟,系统会默认给你设置为 15分钟
 * 代码逻辑看这里:WorkSpec.setPeriodic()
 */
fun submitPeriodicWorker(context: Context) {
    // 周期性的任务,每次间隔30分钟
    val request = PeriodicWorkRequest.Builder(DemoWork::class.java, 30, TimeUnit.MINUTES)
        .build()
    WorkManager.getInstance(context).enqueue(request)
}

注意:

  1. DemoWork:重写doWork来执行任务

  2. 按照上述方式创建的任务,执行间隔为每15分钟一次

  3. 重复任务的间隔时间最低为15分钟,超过这个时间间隔,系统将会默认设置为15分钟

5、设置任务的约束条件

/**
 * 设置约束
 * 如果当前不满足约束条件,则这个任务会处于挂起状态,等待满足约束条件时才会触发
 */
fun buildConstraintsWorker(fragment: Fragment) {
    val config = Constraints.Builder()
        .setRequiresDeviceIdle(true)    // 是否设备空闲时触发
        .setRequiresCharging(true)      // 是否要求充电时触发
        .setRequiredNetworkType(NetworkType.NOT_REQUIRED)   // 对网络状态的现实策略
        .setRequiresBatteryNotLow(true) // 要求设备不需要处于低电量(通常是20%,设备厂商设置的)
        .setRequiresStorageNotLow(true) // 要求设备空间充足
        .addContentUriTrigger(Uri.parse(""), false) // 监听URI发生变化,比如在图库里面插入了一张图片
        .build()
    val request = PeriodicWorkRequestBuilder(24, TimeUnit.SECONDS)
        .setConstraints(config)
        .build()
    WorkManager.getInstance(fragment.requireContext()).enqueue(request)
}

6、链式任务

object ChainWorker {

    class A(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
        override fun doWork(): Result {
            val data = Data.Builder().putString("data", "A任务的结果").build()
            return Result.success(data)
        }
    }

    class B(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
        override fun doWork(): Result {
            val string = inputData.getString("data")
            // string就是A传递过来的数据
            return Result.success()
        }
    }

    class C(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
        override fun doWork(): Result {
            return Result.success()
        }
    }

    class D(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
        override fun doWork(): Result {
            return Result.success()
        }
    }

    class E(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
        override fun doWork(): Result {
            return Result.success()
        }
    }

    /**
     * 创建链式任务
     * 注意:第一个任务失败时,第二个任务也不会继续走了
     */
    fun chainWorker(context: Context) {
        val a = OneTimeWorkRequest.Builder(A::class.java).build()
        val b = OneTimeWorkRequest.Builder(B::class.java).build()
        // 先执行a,再执行b
        WorkManager.getInstance(context).beginWith(a)
            .then(b)
            .enqueue()
    }

    /**
     * 同时执行
     */
    fun submitZipTask(context: Context){
        val a = OneTimeWorkRequestBuilder().build()
        val b = OneTimeWorkRequestBuilder().build()
        WorkManager.getInstance(context).beginWith(listOf(a,b)).enqueue()
    }

    /**
     * 混合链式任务
     * AB串行,CD串行,两个串之间并行,最后再执行e
     */
    fun buildCombineWorker(context: Context) {
        val a = OneTimeWorkRequest.Builder(A::class.java).build()
        val b = OneTimeWorkRequest.Builder(B::class.java).build()
        val c = OneTimeWorkRequest.Builder(C::class.java).build()
        val d = OneTimeWorkRequest.Builder(D::class.java).build()
        val e = OneTimeWorkRequest.Builder(E::class.java).build()

        val chain1 = WorkManager.getInstance(context).beginWith(a).then(b)
        val chain2 = WorkManager.getInstance(context).beginWith(c).then(d)

        WorkContinuation.combine(listOf(chain1,chain2)).then(e).enqueue()
    }

}

7、执行状态监听

  1. 实时监听执行状态回调

通过liveData的形式来获取当前Work的执行结果,liveData需要监听宿主的生命周期,因此需要传入lifeCycler对象

WorkManager.getInstance(fragment.requireContext()).getWorkInfoByIdLiveData(request.id).observe(fragment) { workInfo ->
    val state = workInfo.state
}
  1. 获取任务的当前状态

通过request.getId()可以获取到work的任务id,然后通过这个id即可获取当前任务的状态,注意这个id时系统生成的,不需要用户手动创建

val workInfo = WorkManager.getInstance(fragment.requireContext()).getWorkInfoById(request.id).get()

8、设置退避策略

/**
 * 创建只执行一次的任务
 *
 * 注意:退避策略需要配合Result.retry()
 * 如果直接返回了Result.failure(),则退避策略不会生效的
 */
fun createOneTimeWork(context: Context) {
    val tag = "多个任务可以设置一个TAG,然后可以通过这一个TAG批量获取任务"
    val inputData = Data.Builder().putString("inputData", "这是传入的数据").build()
    val request = OneTimeWorkRequest.Builder(OneTimeWork::class.java)
        .setInitialDelay(10, TimeUnit.MICROSECONDS)  // 10毫秒后再执行
        .keepResultsForAtLeast(2, TimeUnit.DAYS)     // 任务至少会保留一天,WorkManager默认会保留一天的执行结果,可以通过这个变量修改执行结果默认的保存时间
        .setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.MINUTES) // 设置退避策略,比如失败一次后,间隔10分钟后再试,再失败则间隔20分钟,30分钟,40分钟以此类推
        .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 2, TimeUnit.MINUTES)  //指数级增长,失败一次后,间隔10分钟,20分钟,40分钟,80分钟,160分钟。。。
        .setInputData(inputData)
        .addTag(tag)
        .build()
    val id = request.id
    WorkManager.getInstance(context).enqueue(request)
    // 根据任务TAG取消任务
    WorkManager.getInstance(context).cancelAllWorkByTag(tag)
    // 根据任务id取消任务
    WorkManager.getInstance(context).cancelWorkById(id)
}

注意:

  1. 退避策略需要结合 Result.retry()执行,如果返回的是success()或者是failure(),则退避策略无效

  2. keepResultsForAtLeast可以设置任务执行结果的保留时间,2.5版本以下是默认保留7天,2.5以上默认是保留一天,如果用户设置了这个值,则至少会保留这个值的时间,同时为了防止内存占用过大,不建议设置太大的值

9、创建全局唯一任务

/**
 * 创建唯一任务
 * ExistingWorkPolicy.REPLACE:如果队列之前有任务,直接干掉,传入新的
 * ExistingWorkPolicy.KEEP:如果之前有任务还在,本次插入无效
 * ExistingWorkPolicy.APPEND:拼到上一个任务的后面
 */
fun buildSingleWorker(fragment: Fragment) {
    val singleTaskName = "唯一的任务id"
    val request = OneTimeWorkRequest.Builder(OneTimeWorker.OneTimeWork::class.java).build()
    // 创建唯一任务
    WorkManager.getInstance(fragment.requireContext()).beginUniqueWork(singleTaskName, ExistingWorkPolicy.REPLACE, request).enqueue()
    WorkManager.getInstance(fragment.requireContext()).enqueueUniqueWork(singleTaskName, ExistingWorkPolicy.REPLACE, request)

    // 监听这个唯一任务,根据任务名称来监听
    WorkManager.getInstance(fragment.requireContext()).getWorkInfosForUniqueWorkLiveData(singleTaskName).observe(fragment){ workInfoList->
        workInfoList.forEach {
            // 这里可以根据任务的id,来获取指定的任务
        }
    }
}

注意:

  1. 创建任务时,要求传入任务策略:

    1. ExistingWorkPolicy.REPLACE :表示入队任务队列里面存在这个任务,则会执行替换操作

    2. ExistingWorkPolicy.KEEP:如果之前有任务还在,本次插入无效

    3. ExistingWorkPolicy.APPEND:拼到上一个任务的后面

  2. 监听任务执行过程,因为存在拼接的情况,监听的任务回调是一个list,此时可以根据创建时的id来判断具体是哪一个任务

10、常见问题

  1. 应用被杀死后无法执行的,当应用重新启动后,之前提交的work会被激活,这个时候需要判断任务是否已经添加到队列中,避免重复添加

  2. 应用启动后,由于task会被立即激活,而任务中需要依赖的某些参数可能还没有初始化,这个时候需要判空

  3. Work的激活时机在Application的onCreate之后,因此可以考虑在Application中初始化需要的参数

你可能感兴趣的:(android,workmanager)