如何正确取消CoroutineWorker

How to proper cancel 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.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() 等来对信号量、通道执行相同的操作。