WorkManager 协程实例:等待任务完成&自定义超时

252 阅读2分钟

WorkManager 有十分钟超时时间,但由于节电等原因须提前终止;同时要求代码模块化,便于维护的同时 doWork 函数简洁清晰,不含业务本身,便于迁移和复用其他部分的代码。 业务需要使用一个极其耗电的生物传感器,分秒必争,一定要在预测失败的时候立即注销传感器来省电。传感器需要注册和注销,结束时,无论如何都得确保执行了注销函数。 本文对上述内容进行抽象,只关注 WorkManager 的 doWork 部分。

我们先创建一个 Work 类。

class BloodPressureWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {}

首先我们需要创建一个 Job 绑定上定时任务。

private val job = Job()

然后我们将定时任务与协程结合,之后会套上这个协程。

private val scope = CoroutineScope(Dispatchers.Default + job)

最后定义一个 WorkManager 返回结果,让传感器事件返回的结果最后传到 doWork 函数里面。

private val resultDeferred = CompletableDeferred<Result>()

然后我们写 doWork 函数。用了定时任务,在 50 秒后,如果工作还在运行,则运行终止任务。terminate() 包括注销传感器和返回失败。

如果正常运行,则等待任务完成并使用最终的返回值扔给 doWork。正常运行可控,因此终止任务写在对应函数中。因此代码如下:

override fun doWork(): Result {
        val result = runBlocking {
            scope.launch {
                delay(TimeUnit.SECONDS.toMillis(50))
                if (isActive) {
                    terminate()
                }
            }
            // 任务开始
            registerSensor()
            readSensorEvent()
            // 等待结果
            resultDeferred.await()
        }
        return result
    }

readSensorEvent 函数包括完整的执行代码,最后执行传感器的注销函数,终止掉 Job 然后返回成功值。

resultDeferred.complete(Result.success())

完整的代码如下:

private val job = Job()
private val scope = CoroutineScope(Dispatchers.Default + job)

class BpWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
    private val resultDeferred = CompletableDeferred<Result>()

    override fun doWork(): Result {
        val result = runBlocking {
            scope.launch {
              
 delay(TimeUnit.SECONDS.toMillis(50))
                if (isActive) {
                    terminate()
                }
            }
            // 任务开始
            registerSensor()
            readSensorEvent()
            // 等待结果
            resultDeferred.await()
        }
        return result
    }

    private fun registerSensor() {
        Log.i(tag, "REGISTER SENSOR")
        sensorManager.registerListener(sensorListener, Sensor.PPG_BP.id, sensorParam)
        Log.i(tag, "REGISTER DONE")
    }
    // ...

    private fun terminate() {
        // 开始强行终止任务
        Log.i(tag, "TERMINATE")
        unregisterSensor()
        // ...
        resultDeferred.complete(Result.failure())
    }