WorkManager的使用

WorkManager可以用来轻松地创建一个任务,然后放手让它在你指定的某种条件下运行。

在项目中引入WorkManager

dependencies {
    def work_version = "1.0.0-alpha11"
    implementation "android.arch.work:work-runtime-ktx:$work_version" 
}

1. 相关类和概念
  • Worker: 定义需要执行的任务,继承此类并在此定义任务。
  • WorkRequest: 代表一个单独的任务。WorkRequest对象指定执行任务的Worker类。还可以给WorkRequest添加更多的细节,指定任务的执行环境。每一个WorkRequest都有一个自动生成的唯一ID,使用该ID可以用来获取任务的执行状态或者取消任务。WorkRequest是一个抽象类,我们只能使用其两个子类 OneTimeWorkRequest (单次任务)或者 PeriodicWorkRequest (周期性任务)。
  • WorkManager:将WorkRequest传给WorkManager来入队任务。当条件合适时将执行任务。
  • WorkInfo: 通过WorkInfo可以获取特定任务的执行状态,并在任务完成后获取其返回值。

2. 基本的使用

首先,继承Worker类,来定义任务:

class MyWorker(context : Context, params : WorkerParameters)
			: Worker(context, params) {

    override fun doWork(): Result {
        // Do the work here
        doTheWork()

        // Indicate success or failure with your return value:
        // (Returning RETRY tells WorkManager to try this task again
        // later; FAILURE says not to try again.)
        return Result.SUCCESS
    }

}

然后,创建一个WorkRequest并通过WorkManager将任务入队:

val myWork = OneTimeWorkRequestBuilder<MyWorker>().build()
WorkManager.getInstance().enqueue(myWork)

WorkManager将会选择一个合适的时机来执行此任务。在大多数情况下,如果不给任务指定执行条件,WorkManager会立即执行这个任务。如果需要检查任务的执行状态,可以通过WorkInfo来动态监测:

WorkManager.getInstance().getWorkInfoByIdLiveData(myWork.id)
                .observe(lifecycleOwner, Observer { workInfo ->
                    // Do something with the status
                    if (workInfo != null && workInfo.state.isFinished) {
                        // ...
                    }
                })
2.1 添加任务执行条件
// Create a Constraints object that defines when the task should run
val myConstraints = Constraints.Builder()
	 .setRequiresDeviceIdle(true)            //要求设备处于空闲状态
	 .setRequiresCharging(true)              //要求设备正在充电
     .setRequiresBatteryNotLow(true)         //要求设备未处于低电量状态
     .setRequiresStorageNotLow(true)         //要求设备未处于低内存状态
     .setRequiredNetworkType(NetworkType.METERED)  //要求设备网络环境
     .build()

// ...then create a OneTimeWorkRequest that uses those constraints
val myWork = OneTimeWorkRequestBuilder<MyWorker>()
        .setConstraints(myConstraints)
        .build()

这样,只有当设备的状态满足上述要求时,任务才有可能执行。

2.2 取消任务
val myWorkId: UUID = myWork.id
WorkManager.getInstance().cancelWorkById(myWorkId)
2.3 标记任务
val myWork = OneTimeWorkRequestBuilder<MyWork>()
    .setConstraints(myConstraints)
    .addTag("cleanup")
    .build()

标记任务后,可以通过 WorkManager.cancelAllWorkByTag()来取消该标记的所有任务。也可以通过WorkerManager.getWorkInfosByTagLiveData()来获取该标记的所有任务的WorkInfo集合。

2.4 周期性任务
val myBuilder =
        PeriodicWorkRequestBuilder<MyWorker>(12, TimeUnit.HOURS)
// ...if you want, you can apply constraints to the builder here...

// Create the actual work object:
val myWork = myBuilder.build()
// Then enqueue the recurring task:
WorkManager.getInstance().enqueue(myWork)

上面的代码定义了一个每12小时执行一次的任务。其实就是使用PeriodicWorkRequest类来创建WorkRequest, 用法和单次任务一样。


3. 进阶用法
3.1 链式任务

你也许希望一次执行多个任务,或者多个任务按照特定的顺序执行。比如有三个OneTimeWorkRequest对象:workAworkBworkC,可以通过以下方式让它们顺序执行:

WorkManager.getInstance()
    .beginWith(workA)
    .then(workB) 
    .then(workC)
    .enqueue()

也可以给beginWith方法和then方法一次传多个OneTimeWorkRequest,如下:

WorkManager.getInstance()
    // First, run all the A tasks (in parallel):
    .beginWith(workA1, workA2, workA3)
    // ...when all A tasks are finished, run the single B task:
    .then(workB)
    // ...then run the C tasks (in any order):
    .then(workC1, workC2)
    .enqueue()

还可以创建两个任务链,然后将它们合到一起。
在这里插入图片描述

如果想创建如上图内容的任务,可以这样:

val chain1 = WorkManager.getInstance()
    .beginWith(workA)
    .then(workB)
val chain2 = WorkManager.getInstance()
    .beginWith(workC)
    .then(workD)
val chain3 = WorkContinuation
    .combine(chain1, chain2)
    .then(workE)
chain3.enqueue()

这样WorkManager会先执行workA,然后执行workB;同样执行workC之后执行workD,等workBworkD都执行完了,再执行workE

3.2 唯一工作顺序

你可以使用beinUniqueWork方法替代beginWith方法来创建一个唯一工作顺序,该方法如下:

beginUniqueWork(String uniqueWorkName,
            ExistingWorkPolicy existingWorkPolicy,
            OneTimeWorkRequest... work)

beginUniqueWork方法接收三个参数,第一个参数用于给该唯一工作顺序指定一个名称,WorkManager一次只发射一个该名称的工作顺序。如果有两个唯一工作顺序拥有相同的名称,怎么办呢? 第二个参数用于解决这个问题,第二个参数是一个枚举变量,它拥有三个值,作用分别如下:

public enum ExistingWorkPolicy {

    /**
	 * 如果已存在相同唯一名称的work,那么用当前的work替代它
     */
    REPLACE,

    /**
     * 如果已存在相同唯一名称的work,忽略新创建的work
     */
    KEEP,

    /**
     * 如果已存在相同唯一名称的work,把新的work加在原work后面,当原work执行完后再执行新work
     */
    APPEND
}

唯一执行顺序可以用以处理出现某任务还没执行完成,又被重复提交的情况。

3.3 输入参数和接收返回值

为了更具灵活性,可以给任务传递参数并接收其结果值。传值使用 WorkRequest.Builder.setInputData()方法,返回结果值使用 Worker.setOutputData()方法,传值和返回值均使用键值对。

例如某一个任务,需要传入xyz三个数值,计算结束后返回结果值。传值的代码如下:

val myData: Data = workDataOf("x" to 42,
                       "y" to 421,
                       "z" to 8675309)

// ...then create and enqueue a OneTimeWorkRequest that uses those arguments
val mathWork = OneTimeWorkRequestBuilder<MathWorker>()
        .setInputData(myData)
        .build()
WorkManager.getInstance().enqueue(mathWork)

接收传值参数并返回计算结果值的代码如下:

class MathWorker(context : Context, params : WorkerParameters)
    : Worker(context, params)  {

    override fun doWork(): Result {
        val x = inputData.getInt("x", 0)
        val y = inputData.getInt("y", 0)
        val z = inputData.getInt("z", 0)

        // ...do the math...
        val result = myCrazyMathFunction(x, y, z);

        //...set the output, and we're done!
        val output: Data = workDataOf("result" to result)
        setOutputData(output)

        return Result.SUCCESS
    }
}

返回值可以通过监听任务的WorkInfo获取:

WorkManager.getInstance().getWorkInfoByIdLiveData(mathWork.id)
        .observe(this, Observer { status ->
            if (status != null && status.state.isFinished) {
                val myResult = status.outputData.getInt("result",
                      myDefaultValue)
                // ... do something with the result ...
            }
        })

对于链式任务,如果上一个任务在返回结果前调用了setOutputData()方法,那么它的下一个任务可以通过getInputData()方法获取上一个任务返回的结果值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值