结构化并发(Structured Concurrency)是Kotlin协程中的一个强大特性,它允许协程以一种更安全和结构化的方式运行。它可以确保协程生命周期与作用域 (scope) 紧密关联,这种模式可以帮助避免内存泄漏,并确保在父作用域取消时,所有子协程也会被正确地取消。
以下是一些典型场景和建议的使用方式:
1. Activity或Fragment中的一次性任务
对于在Activity或Fragment生命周期内只需要执行一次的后台任务,如初始化数据加载,可以使用lifecycleScope
。
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
val result = repository.loadData()
updateUI(result)
}
}
}
2. ViewModel中的持续性任务
对于需要跨Activity重建持续存在的任务,如用户未读消息计数,可以使用viewModelScope
。
class MyViewModel : ViewModel() {
private val repository = MyRepository()
// 使用LiveData来观察数据变化
val data: LiveData<String> = liveData(context = viewModelScope.coroutineContext + Dispatchers.IO) {
// 这里的代码实际上是在viewModelScope的协程内执行的
try {
while (currentCoroutineContext().isActive) { // 检查isActive以确保协程在viewModelScope被取消时停止
val result = repository.loadData() // 假设这是一个异步操作
emit(result) // 发射新的数据给观察者
delay(5000) // 每5秒刷新一次数据
}
} catch (e: Exception) {
// 处理异常,例如viewModelScope被取消
// 可以在这里处理异常,例如通过emit发送错误信息给观察者
}
}
// ViewModel的其他方法...
}
在这个示例中:
liveData 函数接受一个 context 参数,这里我们将它设置为 viewModelScope.coroutineContext + Dispatchers.IO
。这意味着 liveData 块内的代码将在 IO 调度器上执行,而 LiveData 的观察者更新(通过 emit)将在主线程上执行。
while (currentCoroutineContext().isActive)
循环用于周期性地执行数据加载和发射操作,直到 viewModelScope 被取消。
emit(result)
用于将新数据发送给所有观察 LiveData 的观察者。
delay(5000)
用于在每次数据发射后暂停5秒。
请注意,liveData 块中的代码不需要显式启动协程,因为它已经在 viewModelScope 的管理之下。viewModelScope 确保了当 ViewModel 被销毁时,liveData 块内的协程也会被正确取消。
3. 需要在多个组件间共享的协程
如果你有一组协程需要在多个组件(如多个Fragment)间共享,可以创建一个自定义的CoroutineScope
,并在适当的时候取消它。
class SharedViewModel : ViewModel() {
private val sharedScope = CoroutineScope(Dispatchers.Default)
fun loadData() {
sharedScope.launch {
// 共享的协程逻辑
}
}
override fun onCleared() {
super.onCleared()
sharedScope.cancel()
}
}
4. 后台服务中的协程
在Service
中,如果你需要执行后台任务,可以使用自己的CoroutineScope
,确保在服务销毁时取消所有协程。
class MyService : Service() {
private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
serviceScope.launch {
// 后台服务中的协程逻辑
}
return START_NOT_STICKY
}
override fun onDestroy() {
super.onDestroy()
serviceScope.cancel()
}
}
5. 网络请求
对于网络请求,可以在viewModelScope
或lifecycleScope
中启动协程,根据需要选择是否需要持续监听网络状态。
class MyViewModel : ViewModel() {
fun fetchUserData(userId: String) {
viewModelScope.launch {
val userData = networkService.getUserData(userId)
// 更新UI或存储数据
}
}
}
6. 处理用户输入
当响应用户输入时,如按钮点击,可以在lifecycleScope
中启动协程,以确保即使Activity或Fragment重建,用户的操作也能被正确响应。
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
button.setOnClickListener {
lifecycleScope.launch {
// 处理用户点击
}
}
}
}
7. 复杂的并发任务
对于需要同时执行多个并发任务的场景,可以在CoroutineScope
中使用launch
来启动多个协程,并使用join
来等待它们完成。
class MyViewModel : ViewModel() {
fun performMultipleTasks() {
viewModelScope.launch {
val job1 = launch {
// 任务1
}
val job2 = launch {
// 任务2
}
job1.join()
job2.join()
}
}
}
8. 资源清理
在组件销毁时,确保所有协程都被取消,并且资源得到清理。这可以通过在onCleared
(对于ViewModel
)或onDestroy
(对于Activity
和Service
)中取消CoroutineScope
来实现。
通过遵循结构化并发的原则,可以确保我们的应用程序中的协程是可预测的、易于管理的,并且能够正确响应组件的生命周期变化。