如何正确取消 CoroutineWorker

问题描述

我有一个 CoroutineWorker,它需要在用户与我的应用交互时启动和停止,所以我使用了 awaitCancelation() 方法,如下所示:

override suspend fun doWork(): Result {
        try {
            startListen()
            awaitCancellation()
        } finally {
            stopListen()
            return Result.success()
        }
    }

一切正常,但是当我调用 WorkManager.cancel 并取消我的工作时,WorkerWrapper 会抛出一个内部异常:

I/WM-WorkerWrapper: Work [ id=721c463b-15aa-4daa-988d-a4c080915443,tags={ br.com.example.app.workers.MyWorker,WORKER_TAG } ] was cancelled
    java.util.concurrent.CancellationException: Task was cancelled.
        at androidx.work.impl.utils.futures.AbstractFuture.cancellationExceptionWithCause(AbstractFuture.java:1184)
        at androidx.work.impl.utils.futures.AbstractFuture.getDoneValue(AbstractFuture.java:514)
        at androidx.work.impl.utils.futures.AbstractFuture.get(AbstractFuture.java:475)
        at androidx.work.impl.WorkerWrapper$2.run(WorkerWrapper.java:300)
        at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)

我没有找到任何其他看起来更合适的方法来停止我的 CoroutineWorker,同时我认为这不是停止它的正确方法。

如果有人知道避免这种情况或抑制这种异常的另一种方法,我将不胜感激。

解决方法

通常情况下,通过抛出 CancellationException 取消我们的可取消后台任务并没有什么错。通常最容易抛出异常,因为它可以自动传播到整个调用堆栈。 Java 在中断线程时使用相同的技术 - 它抛出 InterruptedException

但是如果你真的需要一种更简洁的方式来通知你的后台服务它应该停止工作,协程库中有很多实用程序可以让你实现这一点。例如,您可以使用 Mutex:

private val mutex = Mutex(true)

override suspend fun doWork(): Result {
        try {
            startListen()
            mutex.lock()
        } finally {
            stopListen()
            return Result.success()
        }
    }

fun stop() {
    mutex.unlock()
}

由于互斥锁最初处于锁定状态,它将在 lock() 处挂起。然后互斥锁在 stop() 中解锁,允许第一个协程继续。您可以对信号量、通道执行相同的操作,方法是创建一个 CompletableJob,然后在其上创建 join() 等。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...