题目
实现一个并发网络请求并处理结果的协程方案
信息
- 类型:问答
- 难度:⭐⭐
考点
结构化并发, 异常处理, 挂起函数组合, 协程作用域管理
快速回答
实现安全并发请求的关键点:
- 使用
coroutineScope或async启动子协程 - 通过
try/catch或CoroutineExceptionHandler处理异常 - 使用
awaitAll()等待所有请求完成 - 在
ViewModel或lifecycleScope中启动协程确保生命周期安全 - 通过
supervisorScope实现子协程独立失败
场景需求
在Android应用中,需要同时发起3个独立网络请求(获取用户数据/商品列表/消息通知),当所有请求成功时合并结果展示,任一请求失败时进行统一错误处理。
核心实现方案
// 在ViewModel中安全启动协程
fun loadAllData() {
viewModelScope.launch(CoroutineExceptionHandler { _, e ->
// 统一错误处理
showError(e)
}) {
try {
// 结构化并发:三个请求同时启动
val result = coroutineScope {
val userDeferred = async { userRepo.getUserData() }
val productDeferred = async { productRepo.getProductList() }
val notificationDeferred = async { notificationRepo.getNotifications() }
// 等待所有请求完成
awaitAll(userDeferred, productDeferred, notificationDeferred)
}
// 处理合并结果
combineResults(result[0], result[1], result[2])
} catch (e: Exception) {
// 补充异常捕获(可选)
}
}
}
// 挂起函数示例
suspend fun UserRepository.getUserData(): UserData {
return withContext(Dispatchers.IO) {
// 模拟网络请求
delay(1000)
UserData(...)
}
}关键原理说明
- 结构化并发:
coroutineScope创建子作用域,所有子协程必须完成后父协程才能继续执行 - 异常传播:默认情况下子协程异常会取消整个作用域,使用
supervisorScope可实现子协程独立失败 - 并发控制:
async启动并发任务,awaitAll()等待所有结果 - 线程调度:
withContext(Dispatchers.IO)确保阻塞操作在IO线程执行
最佳实践
- 作用域管理:在ViewModel中使用
viewModelScope,在Activity中使用lifecycleScope - 异常处理分层:
- 使用
CoroutineExceptionHandler处理未捕获异常 - 在关键位置添加
try/catch处理特定异常
- 使用
- 取消传播:协程取消时自动取消所有子协程
- 超时控制:使用
withTimeout(5000)添加单次请求超时
常见错误
- 错误1:在
launch块直接调用async但不调用await()(导致请求未启动) - 错误2:使用
GlobalScope导致生命周期管理失控 - 错误3:忘记处理
async返回的Deferred异常 - 错误4:在主线程执行阻塞网络请求导致ANR
扩展知识
- 结果聚合优化:使用
listOf(deferred1, deferred2).awaitAll() - 部分成功处理:
supervisorScope { val userReq = async { /* ... */ } val productReq = async { /* ... */ } // 单独处理每个请求结果 userReq.await().onSuccess { /*...*/ } productReq.await().onFailure { /*...*/ } } - Flow替代方案:使用
flow { ... }.zip或combine操作符合并流