协程是否会绕过执行流程?使用suspend实现网络请求的更优方案是什么
现有实现存在的问题
- 协程生命周期无绑定:Activity中直接创建临时
CoroutineScope没有和页面生命周期绑定,页面销毁后协程仍会继续运行,既会造成内存泄漏,后续逻辑如果访问页面资源还会触发崩溃。 - 存在冗余代码:单独启动
async后立刻调用await()属于完全多余的操作,没有任何收益,直接调用suspend方法即可实现等待响应的需求。 - 不支持协程取消:ViewModel中使用
suspendCoroutine封装请求,没有适配协程的取消机制,协程取消后网络请求仍会在后台运行,浪费系统资源。 - 缺失异常处理:网络请求必然存在失败概率,现有代码没有捕获异常,遇到网络错误、请求超时等场景会直接触发应用崩溃。
- 线程调度不合理:在IO调度器启动协程后如果要更新UI还需要手动切回主线程,增加额外的调度代码。
优化后的实现方案
优先推荐使用2.6.0及以上版本的Retrofit,原生支持suspend方法,不需要手动封装协程逻辑,是当前最优的实现方案。
ViewModel层代码
// Retrofit接口直接定义suspend方法,无需手动处理协程 interface ApiService { @GET suspend fun requestAccessUrl(@Url url: String): Response } class MyViewModel: ViewModel() { private val apiService = Retrofit.Builder() // 此处省略Retrofit常规配置 .build() .create(ApiService::class.java) // 直接暴露suspend方法,不需要手动指定调度器 suspend fun requestAccess(url: String): Response { return apiService.requestAccessUrl(url) } // 如果必须封装原有回调式的旧网络请求,使用suspendCancellableCoroutine支持取消 suspend fun requestAccessLegacy(url: String): Response = suspendCancellableCoroutine { cont -> val call = OkHttpClient().newCall(Request.Builder().url(url).build()) // 协程取消时同步取消网络请求,避免资源浪费 cont.invokeOnCancellation { call.cancel() } call.enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { cont.resumeWithException(e) } override fun onResponse(call: Call, response: okhttp3.Response) { cont.resume(response) } }) } }
Activity层代码
private fun prepareUnlockUrl(url: String) { // 使用lifecycleScope绑定Activity生命周期,页面销毁自动取消协程 lifecycleScope.launch { try { val result = mViewModel.requestAccess(url) // lifecycleScope默认运行在主线程,拿到结果后可以直接更新UI Log.i(TAG, "do something with 'result'") when(result) { // 此处处理不同的响应逻辑 } } catch (e: Exception) { // 统一处理请求异常 Log.e(TAG, "请求失败", e) Toast.makeText(this@CurrentActivity, "网络请求失败,请稍后重试", Toast.LENGTH_SHORT).show() } } }
优化点说明
- 自动管理生命周期:
viewModelScope会在ViewModel销毁时自动取消所有子协程,lifecycleScope会在页面销毁时自动取消协程,从根源避免内存泄漏问题。 - 移除冗余操作:去掉了无意义的
async{}.await()写法,仅通过suspend方法的挂起特性就实现了等待响应再执行后续逻辑的需求。 - 支持取消联动:用
suspendCancellableCoroutine替代suspendCoroutine,协程取消时同步终止网络请求,减少不必要的资源消耗。 - 完善异常处理:增加try-catch捕获请求全链路可能出现的异常,避免无预期的崩溃。
- 调度逻辑更合理:主流网络框架的suspend实现已经自动处理了后台线程调度,不需要手动指定
Dispatchers.IO,结果返回后自动回到协程启动的主线程,可直接操作UI。
内容的提问来源于stack exchange,提问作者Faisal
相关产品推荐
相关产品推荐

