题目
Android中如何安全启动协程并处理生命周期?
信息
- 类型:问答
- 难度:⭐⭐
考点
Kotlin协程, Android生命周期感知, 异常处理
快速回答
在Android中使用协程时需注意:
- 使用
lifecycleScope自动绑定生命周期 - 在
ViewModel中使用viewModelScope - 通过
SupervisorJob处理独立任务异常 - 使用
try/catch或CoroutineExceptionHandler捕获异常 - 避免在
onDestroy后更新UI
核心问题
在Android中启动协程时,必须正确处理生命周期以避免内存泄漏和崩溃。主要挑战包括:生命周期绑定、异常处理和线程切换。
解决方案与代码示例
1. 生命周期绑定
// Activity/Fragment中使用(需添加lifecycle-ktx依赖)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
// 自动在DESTROY时取消
val data = fetchData() // 挂起函数
updateUI(data)
}
}
}
// ViewModel中使用
class MyViewModel : ViewModel() {
init {
viewModelScope.launch {
// 当ViewModel cleared时自动取消
loadData()
}
}
}2. 异常处理
// 方案1:try/catch包裹
lifecycleScope.launch {
try {
riskyOperation()
} catch (e: Exception) {
showError(e)
}
}
// 方案2:SupervisorJob(子协程独立失败)
val customScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
// 方案3:全局异常处理器
val handler = CoroutineExceptionHandler { _, throwable ->
Log.e("CoroutineError", "Caught: $throwable")
}
viewModelScope.launch(handler) { riskyTask() }最佳实践
- 作用域选择:
- UI操作使用
lifecycleScope或viewModelScope - 长时后台任务使用自定义作用域(需手动取消)
- UI操作使用
- 线程调度:
- UI更新:
Dispatchers.Main - CPU密集型:
Dispatchers.Default - IO操作:
Dispatchers.IO
- UI更新:
- 取消处理:检查
isActive或调用ensureActive()
常见错误
- 内存泄漏:在Activity中使用
GlobalScope导致无法自动取消 - 生命周期问题:在
onDestroy后尝试更新UI - 异常传播:未捕获异常导致应用崩溃(非SupervisorJob下子协程异常会传播)
- 线程阻塞:在
Dispatchers.Main执行耗时操作导致ANR
扩展知识
- 结构化并发:通过作用域层级管理协程父子关系
- StateFlow:配合协程实现生命周期感知的状态更新
- LiveData协程扩展:
liveData { ... }构建器自动取消数据流 - 测试:使用
TestCoroutineDispatcher控制虚拟时间
原理说明
当使用lifecycleScope时,内部通过LifecycleCoroutineScope实现:
// 简化实现原理
fun LifecycleOwner.lifecycleScope(): CoroutineScope {
return LifecycleCoroutineScopeImpl(
lifecycle,
SupervisorJob() + Dispatchers.Main.immediate
).apply {
lifecycle.addObserver(object : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun destroy() {
cancel() // 生命周期结束时取消所有协程
}
})
}
}