关于 android-coroutines
面向 Android Kotlin 开发者的协程实践技能,提供结构化并发、生命周期安全、错误处理和测试方面的规则与模式,适用于数据获取及后台处理等任务。
主要功能
- 01注入 CoroutineDispatcher
- 02安全收集 Flow
- 03使用 viewModelScope
- 04处理取消与异常
- 05用 callbackFlow 转换回调
- 06使用 runTest 测试
适用场景
- 01实现 API 或数据库数据获取
- 02将回调 API 转为 Flow
- 03避免生命周期相关的任务泄漏
- 04测试协程与 ViewModel
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ new-silvermoon/ awesome-android-agent-skills --skill android-coroutines安装后在 Claude Code 中输入/android-coroutines确认已加载。
详细介绍
Android Coroutines 专家技能
此技能提供在 Android 上编写生产级 Kotlin Coroutines 代码的权威规则和模式。它遵循结构化并发、生命周期安全及现代最佳实践(2025 年标准)。
职责
- 异步逻辑:实现挂起函数、Dispatcher 管理和并行执行。
- 响应式流:实现
Flow、StateFlow、SharedFlow和callbackFlow。 - 生命周期集成:管理作用域(
viewModelScope、lifecycleScope)和安全收集(repeatOnLifecycle)。 - 错误处理:实现
CoroutineExceptionHandler、SupervisorJob和合理的try-catch层级。 - 可取消性:通过
ensureActive()确保长时间运行的操作能够协作式取消。 - 测试:配置
TestDispatcher和runTest。
适用场景
用户提出以下需求时启用此技能:
- “从 API/数据库获取数据。”
- “执行后台处理。”
- “修复与线程/任务相关的内存泄漏。”
- “将监听器/回调转换为 Coroutines。”
- “实现 ViewModel。”
- “处理 UI 状态更新。”
关键规则与限制
1. 注入 Dispatcher(可测试性)
- 绝不在类中硬编码 Dispatcher(例如
Dispatchers.IO、Dispatchers.Default)。 - 始终通过构造函数注入
CoroutineDispatcher。 - 为方便起见,构造函数参数中默认使用
Dispatchers.IO,但要允许覆盖。
// CORRECT
class UserRepository(
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) { ... }
// INCORRECT
class UserRepository {
fun getData() = withContext(Dispatchers.IO) { ... }
}
2. 主线程安全
- Data 层或 Domain 层定义的所有挂起函数都必须是主线程安全的。
- 一次性调用应以
suspend函数形式提供。 - 数据变更应以
Flow形式提供。 - 调用方(ViewModel)应能从
Dispatchers.Main调用这些函数,而不会阻塞 UI。 - 在 repository 实现中使用
withContext(dispatcher),将执行切换到后台。
3. 生命周期感知的收集
- 绝不要直接在
lifecycleScope.launch或launchWhenStarted(已弃用/不安全)中收集 flow。 - 在 Activity 或 Fragment 中收集 flow 时,始终使用
repeatOnLifecycle(Lifecycle.State.STARTED)。
// CORRECT
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { ... }
}
}
4. 使用 ViewModel 作用域
- 在 ViewModel 中使用
viewModelScope启动协程。 - 不要向 View 暴露 ViewModel 的挂起函数。ViewModel 应暴露
StateFlow或SharedFlow,供 View 观察。
5. 封装可变状态
- 绝不公开暴露
MutableStateFlow或MutableSharedFlow。 - 使用
.asStateFlow()或向上转型,将它们作为只读StateFlow或Flow暴露。
6. 禁止使用 GlobalScope
- 绝不使用
GlobalScope。它会破坏结构化并发并导致泄漏。 - 如果任务必须在当前作用域结束后继续运行,请使用注入的
applicationScope(与 Application 生命周期绑定的自定义作用域)。
7. 异常处理
- 绝不要在通用的
catch (e: Exception)块中捕获CancellationException,却不重新抛出。 - 仅当明确重新抛出
CancellationException时才使用runCatching。 CoroutineExceptionHandler仅用于顶层协程(launch内)。它在async或子协程中不起作用。
8. 可取消性
- 协程采用协作式取消。除非检查取消状态,否则它们不会立即停止。
- 在紧密循环中(例如处理大型列表、读取文件),始终调用
ensureActive()或yield()来检查是否已取消。 delay()和withContext()等标准函数本身就支持取消。
9. 回调转换
- 使用
callbackFlow将基于回调的 API 转换为 Flow。 - 在
callbackFlow代码块末尾始终使用awaitClose注销监听器。
代码模式
使用 Flow 的 Repository 模式
class NewsRepository(
private val remoteDataSource: NewsRemoteDataSource,
private val externalScope: CoroutineScope, // For app-wide events
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) {
val newsUpdates: Flow<List<News>> = flow {
val news = remoteDataSource.fetchLatestNews()
emit(news)
}.flowOn(ioDispatcher) // Upstream executes on IO
}
并行执行
suspend fun loadDashboardData() = coroutineScope {
val userDeferred = async { userRepo.getUser() }
val feedDeferred = async { feedRepo.getFeed() }
// Wait for both
DashboardData(
user = userDeferred.await(),
feed = feedDeferred.await()
)
}
使用 runTest 进行测试
@Test
fun testViewModel() = runTest {
val testDispatcher = StandardTestDispatcher(testScheduler)
val viewModel = MyViewModel(testDispatcher)
viewModel.loadData()
advanceUntilIdle() // Process coroutines
assertEquals(expectedState, viewModel.uiState.value)
}
评论
支持 Markdown