SuperAPI AI 工具集

kotlin-coroutines-structured-concurrency

编程与开发

查看项目

更新于 10-16 05:38

技能介绍

用途

该技能用于在编写或审查 Kotlin 代码时,识别并修复结构化并发相关的问题。触发场景包括:类中存储 CoroutineScope 属性、在 init 块中启动协程、从非挂起 API 启动协程、调用 runBlocking、以及宽泛捕获异常等。

核心原则

结构良好的协程是自包含的异步工作单元——单一入口、单一出口,作用域绑定到调用点已知的生命周期。作用域通常应绑定调用方生命周期,而不是作为被调用方的属性存储。存储的 CoroutineScope 是一个强烈的审查信号:该类必须证明自己拥有取消、错误上报、重启行为和生命周期。大多数仓库、管理器、用例和数据源无法证明这一点,因此应改为暴露 suspend API。修复方式几乎总是相同的:让 API 变为 suspend,由调用方拥有作用域。

主要能力

技能识别并给出修复方案的反模式包括:

  1. 将 CoroutineScope 存储为属性。修复:改为 suspend 函数,由调用方决定作用域、错误处理和取消语义。

  2. init 块中启动协程。构造时产生副作用,调用方无法等待、无法看到错误、无法取消。修复:提供显式 suspend 的 init() 引导方法。

  3. 非 UI 类的即发即弃。非挂起的公共函数启动到类拥有的作用域,调用方得不到结果、错误、取消或工作是否执行的保证。修复:改为 suspend API。

  4. 非注入的存储作用域,如 MainScope() 或 CoroutineScope(...),生命周期无人拥有且永久存活。修复:替换为 suspend API。

  5. DI 绑定的单例或初始化器在构造/init/initialize() 中启动协程。启动时间不确定、生命周期不可观察、无停止/重启路径、无法 grep 调用点。技能给出三种优先方案:将观察反转进消费者、使用 WorkManager/BGTaskScheduler 调度、或在显式命名调用点启动。

  6. 吞掉 CancellationException。宽泛捕获 Exception/Throwable 或使用 runCatching 而不重新抛出,会把取消变成静默成功。技能给出多种可接受写法,如先单独捕获 CancellationException 并重新抛出、条件重抛、ensureActive()、runCatching 加显式守卫、getOrThrow 等。

  7. runBlocking。在可挂起或生命周期作用域的应用路径中阻塞线程,破坏结构化并发。修复:应用代码改为 suspend;测试使用 runTest;仅在 CLI main、Java 互操作、无挂起替代的框架回调等合法阻塞边界保留。

使用方式

该技能以审查指南形式使用:在编写或审查 Kotlin 代码时,对照上述反模式清单检查,并应用对应的修复方案。技能还提供审查诊断问题:启动时刻在哪里定义?谁能观察工作是否运行?谁能停止或重启?读者能否 grep 到启动点?

适用场景

适用于 Kotlin 协程代码的编写与代码审查,尤其是涉及仓库、管理器、用例、数据源、DI 单例、初始化器、ViewModel 等状态持有者的场景。

限制

技能明确说明 UI 与状态持有者边界是唯一例外:UI 框架不可挂起,状态持有者(ViewModel、Decompose Component、feature model 等)负责将一次性 UI 事件转换为绑定 UI 生命周期的异步工作。该例外需同时满足三个条件:是 UI 表面的状态持有者、使用生命周期绑定的作用域、调用方确实是 UI 事件。仓库/用例/数据源层仍应暴露 suspend API。此外,有意为之的本地超时(捕获自身 withTimeout 的 TimeoutCancellationException 并转换为领域结果)是常见例外,但应保持捕获范围窄且靠近超时处。