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())
}