kotlin-concurrency-expert by new-silvermoon/awesome-android-agent-skills
npx skills add https://github.com/new-silvermoon/awesome-android-agent-skills --skill kotlin-concurrency-expert通过应用结构化并发、生命周期安全、正确的作用域划分以及现代最佳实践,以最小的行为变更来审查和修复 Android 代码库中的 Kotlin 协程问题。
kotlinx-coroutines-android 版本、lifecycle-runtime-ktx 版本。viewModelScope、lifecycleScope、自定义作用域或无作用域)。Dispatchers.Main)还是旨在主线程外运行的(Dispatchers.IO、Dispatchers.Default)。广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
优先选择在满足结构化并发和生命周期安全的同时,保留现有行为的编辑。
常见修复:
withContext(Dispatchers.IO) 或 Dispatchers.Default;确保挂起函数是主线程安全的。GlobalScope 替换为生命周期绑定的作用域(viewModelScope、lifecycleScope 或注入的 applicationScope)。launchWhenStarted 替换为 repeatOnLifecycle(Lifecycle.State.STARTED)。MutableStateFlow / MutableSharedFlow;暴露只读的 StateFlow 或 Flow。catch (e: Exception) 块重新抛出 CancellationException。ensureActive() 或 yield() 以实现协作式取消。awaitClose 清理的 callbackFlow 将监听器转换为流。CoroutineDispatcher 以提高可测试性。// 正确:注入调度器
class UserRepository(
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) {
suspend fun fetchUser() = withContext(ioDispatcher) { ... }
}
// 错误:硬编码调度器
class UserRepository {
suspend fun fetchUser() = withContext(Dispatchers.IO) { ... }
}
// 正确:使用 repeatOnLifecycle
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state -> updateUI(state) }
}
}
// 错误:直接收集(不安全,已弃用)
lifecycleScope.launchWhenStarted {
viewModel.uiState.collect { state -> updateUI(state) }
}
// 正确:暴露只读的 StateFlow
class MyViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
}
// 错误:暴露可变状态
class MyViewModel : ViewModel() {
val uiState = MutableStateFlow(UiState()) // 泄漏了可变性
}
// 正确:重新抛出 CancellationException
try {
doSuspendWork()
} catch (e: CancellationException) {
throw e // 必须重新抛出!
} catch (e: Exception) {
handleError(e)
}
// 错误:吞没了取消异常
try {
doSuspendWork()
} catch (e: Exception) {
handleError(e) // CancellationException 被吞没!
}
// 正确:在紧密循环中检查取消
suspend fun processLargeList(items: List<Item>) {
items.forEach { item ->
ensureActive() // 检查取消
processItem(item)
}
}
// 错误:非协作式(忽略取消)
suspend fun processLargeList(items: List<Item>) {
items.forEach { item ->
processItem(item) // 从不检查取消
}
}
// 正确:带有 awaitClose 的 callbackFlow
fun locationUpdates(): Flow<Location> = callbackFlow {
val listener = LocationListener { location ->
trySend(location)
}
locationManager.requestLocationUpdates(listener)
awaitClose { locationManager.removeUpdates(listener) }
}
| 作用域 | 使用时机 | 生命周期 |
|---|---|---|
viewModelScope | ViewModel 操作 | 随 ViewModel 清除 |
lifecycleScope | Activity/Fragment 中的 UI 操作 | 随生命周期所有者销毁 |
repeatOnLifecycle | UI 中的 Flow 收集 | 随生命周期状态启动/停止 |
applicationScope(注入) | 应用范围内的后台工作 | 应用生命周期 |
GlobalScope | 切勿使用 | 破坏结构化并发 |
@Test
fun `loading data updates state`() = runTest {
val testDispatcher = StandardTestDispatcher(testScheduler)
val repository = FakeRepository()
val viewModel = MyViewModel(repository, testDispatcher)
viewModel.loadData()
advanceUntilIdle()
assertEquals(UiState.Success(data), viewModel.uiState.value)
}
每周安装量
199
代码仓库
GitHub 星标数
552
首次出现
2026年1月27日
安全审计
安装于
opencode182
codex174
gemini-cli167
github-copilot162
kimi-cli149
amp147
Review and fix Kotlin Coroutines issues in Android codebases by applying structured concurrency, lifecycle safety, proper scoping, and modern best practices with minimal behavior changes.
kotlinx-coroutines-android version, lifecycle-runtime-ktx version.viewModelScope, lifecycleScope, custom scope, or none).Dispatchers.Main) or intended to run off the main thread (Dispatchers.IO, Dispatchers.Default).Prefer edits that preserve existing behavior while satisfying structured concurrency and lifecycle safety.
Common fixes:
withContext(Dispatchers.IO) or Dispatchers.Default; ensure suspend functions are main-safe.GlobalScope with a lifecycle-bound scope (viewModelScope, lifecycleScope, or injected applicationScope).launchWhenStarted with repeatOnLifecycle(Lifecycle.State.STARTED).MutableStateFlow / ; expose read-only or .// CORRECT: Inject dispatcher
class UserRepository(
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) {
suspend fun fetchUser() = withContext(ioDispatcher) { ... }
}
// INCORRECT: Hardcoded dispatcher
class UserRepository {
suspend fun fetchUser() = withContext(Dispatchers.IO) { ... }
}
// CORRECT: Use repeatOnLifecycle
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state -> updateUI(state) }
}
}
// INCORRECT: Direct collection (unsafe, deprecated)
lifecycleScope.launchWhenStarted {
viewModel.uiState.collect { state -> updateUI(state) }
}
// CORRECT: Expose read-only StateFlow
class MyViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
}
// INCORRECT: Exposed mutable state
class MyViewModel : ViewModel() {
val uiState = MutableStateFlow(UiState()) // Leaks mutability
}
// CORRECT: Rethrow CancellationException
try {
doSuspendWork()
} catch (e: CancellationException) {
throw e // Must rethrow!
} catch (e: Exception) {
handleError(e)
}
// INCORRECT: Swallows cancellation
try {
doSuspendWork()
} catch (e: Exception) {
handleError(e) // CancellationException swallowed!
}
// CORRECT: Check for cancellation in tight loops
suspend fun processLargeList(items: List<Item>) {
items.forEach { item ->
ensureActive() // Check cancellation
processItem(item)
}
}
// INCORRECT: Non-cooperative (ignores cancellation)
suspend fun processLargeList(items: List<Item>) {
items.forEach { item ->
processItem(item) // Never checks cancellation
}
}
// CORRECT: callbackFlow with awaitClose
fun locationUpdates(): Flow<Location> = callbackFlow {
val listener = LocationListener { location ->
trySend(location)
}
locationManager.requestLocationUpdates(listener)
awaitClose { locationManager.removeUpdates(listener) }
}
| Scope | Use When | Lifecycle |
|---|---|---|
viewModelScope | ViewModel operations | Cleared with ViewModel |
lifecycleScope | UI operations in Activity/Fragment | Destroyed with lifecycle owner |
repeatOnLifecycle | Flow collection in UI | Started/Stopped with lifecycle state |
applicationScope (injected) | App-wide background work | Application lifetime |
GlobalScope |
@Test
fun `loading data updates state`() = runTest {
val testDispatcher = StandardTestDispatcher(testScheduler)
val repository = FakeRepository()
val viewModel = MyViewModel(repository, testDispatcher)
viewModel.loadData()
advanceUntilIdle()
assertEquals(UiState.Success(data), viewModel.uiState.value)
}
Weekly Installs
199
Repository
GitHub Stars
552
First Seen
Jan 27, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode182
codex174
gemini-cli167
github-copilot162
kimi-cli149
amp147
Next.js 15+ 最佳实践指南:文件约定、RSC边界、异步模式与性能优化
928 周安装
Google Workspace CLI 项目经理角色技能 - 自动化项目协调与任务管理工具
7,400 周安装
响应式设计实战指南:掌握CSS容器查询、流式排版与移动优先布局
7,200 周安装
Google Workspace Events CLI 命令:管理订阅与事件流(gws-events)
7,300 周安装
Solidity 智能合约安全指南:防范重入攻击、溢出漏洞与访问控制
7,400 周安装
OpenAPI 3.1 规范生成与验证 | API 文档、SDK 生成、契约设计
7,400 周安装
错误处理模式指南:构建弹性应用程序的稳健错误处理策略
7,300 周安装
MutableSharedFlowStateFlowFlowcatch (e: Exception) blocks rethrow CancellationException.ensureActive() or yield() in tight loops for cooperative cancellation.callbackFlow with proper awaitClose cleanup.CoroutineDispatcher via constructor for testability.| NEVER USE |
| Breaks structured concurrency |