fix(cw): stop swallowing coroutine cancellation on pause/resume
用户反馈: 暂停再恢复时经常弹出 "CW decode failed: The coroutine scope left the composition"。 根因: 暂停 (isListening=false) 使 LaunchedEffect 重启、旧的采集协程被 取消。若此刻 decodeWindow 正在 withContext(Dispatchers.Default) 里做 ONNX 推理, 取消传播时会抛出 LeftCompositionCancellationException (message 即 "The coroutine scope left the composition", 是 CancellationException 的子类)。processBuffer 的 catch (Throwable) 把它当成解码失败吞掉, 既误报了 错误横幅, 又破坏了协程取消的正常传播。 修复: - processBuffer 的 decodeWindow catch 里, CancellationException 直接 rethrow (暂停导致的中断是正常流程, 不是错误)。 - archiveDecode 调用同样包 try-catch, CancellationException rethrow, 其余异常只记日志不崩溃。 这是协程的标准纪律: 永远不要把 CancellationException 当业务异常吞掉。 版本: 4.5.5 -> 4.5.7 (versionCode 460)。此前多次删 v4.5.5 tag 重打导致 release 页面累积 12 个 draft 草稿, 已全部清除。
This commit is contained in:
1 parent
2e1d8b9c00
commit
26c714fc24
2 files changed
+14
-3
No files matched your search
@@ -26,6 +26,7 @@ import com.rtbishop.look4sat.core.domain.cw.CwCtcDecoder
|
||||
import com.rtbishop.look4sat.core.domain.cw.CwDeepBuffer
|
||||
import com.rtbishop.look4sat.core.domain.cw.CwDeepSpectrogram
|
||||
import com.rtbishop.look4sat.core.domain.cw.ICwDecoder
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -185,7 +186,12 @@ class CwDeepDecoder(context: Context) : ICwDecoder {
|
||||
if (archiveSize >= ARCHIVE_THRESHOLD) {
|
||||
val audio = archiveBuffer.copyOf(archiveSize)
|
||||
archiveSize = 0
|
||||
archiveDecode(audio)
|
||||
try {
|
||||
archiveDecode(audio)
|
||||
} catch (t: Throwable) {
|
||||
if (t is CancellationException) throw t
|
||||
Log.e(TAG, "archive decode failed", t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,6 +205,11 @@ class CwDeepDecoder(context: Context) : ICwDecoder {
|
||||
try {
|
||||
decodeWindow(buffer.snapshot())
|
||||
} catch (t: Throwable) {
|
||||
// Cancellation is normal when the user pauses: the capture coroutine
|
||||
// is cancelled while an inference is in flight. Never swallow it as
|
||||
// a decode error — rethrow so the coroutine machinery works, and do
|
||||
// not flash a spurious "decode failed" banner.
|
||||
if (t is CancellationException) throw t
|
||||
Log.e(TAG, "inference failed", t)
|
||||
_errorMessage.value = "CW decode failed: ${t.message ?: t.javaClass.simpleName}"
|
||||
runCatching {
|
||||
|
||||
Reference in new issue
Block a user