SupervisorJob

默认情况下,协程作用域用的是 Job(),父子关系就像一根绳上的蚂蚱——任意一个子协程抛异常,整个作用域(包括其他正常跑着的兄弟协程)都会被取消。对于”加载多条测线、其中一条失败不该拖累其他”的场景,这就太脆了。

换成 SupervisorJob(),子协程的失败不会往上传播,其他兄弟照常跑:

class RadarViewModel {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
}

@PreDestroy

协程要是不主动取消,它会一直挂着,引用着 ViewModel / UI,造成内存泄漏,甚至组件都销毁了还跑回来刷新一个已经失效的 UI。把 scope 的取消绑到生命周期上:

@PreDestroy
fun onDestroy() {
scope.cancel() // 取消所有子协程
}

Mutex 加超时

共有 / 复合病害的计算可能短时间里被触发好几次(连续编辑),不加锁的话,两个并发计算会互相把结果覆盖掉。用 Mutex 互斥,保证同一时刻只有一个在算:

private val mutex = Mutex()

suspend fun recalc() = withTimeoutOrNull(60_000) {
mutex.withLock {
// 计算...
}
}
// 超时返回 null,降级处理,避免无限等待卡死

外面再套一层 withTimeout(60s) 是兜底:万一锁被长期占着(比如某个计算卡死了),不会让调用方一直干等,超时就降级处理。

这几个点其实都是在补协程工程化上的漏洞:SupervisorJob 隔离失败、生命周期绑定取消防泄漏、Mutex 加超时防并发覆盖和死等。几样补齐了,协程放到生产环境才让人放心。