Kotlin+Retrofit实现多线程并行执行多API请求优化加载性能
多API并行请求优化方案
现有代码存在的问题
你当前的实现本身用了Retrofit的enqueue异步请求,本身不会串行执行,但存在两个核心问题导致感知上加载慢:
- 所有API请求共用同一个全局
MutableLiveData实例,后返回的请求结果会覆盖之前的结果,你既无法区分返回结果对应哪个接口,也没法统一判断所有请求是否全部完成,只能收到一次结果就更新一次UI,重复渲染自然会卡 - 没有做请求结果聚合,需要等所有接口返回才能完整渲染页面时,当前写法没有统一的完成回调,会拉长感知上的加载时间
方案1:基于ExecutorService实现并行请求
适合你要求的用executors、原生线程能力实现的需求:
Repository层修改
class Repository { // 配置和请求数量匹配的固定线程池,比如5次请求就设5个核心线程 private val apiExecutor = Executors.newFixedThreadPool(5) // 把原有异步回调的请求改成同步执行,方便提交到线程池 fun callApiOneSync(): Resource<Model> { return try { val response = AppMain.restClient?.services?.callApiOne()?.execute() if (response?.isSuccessful == true) { Resource.successResp(response) } else { Resource.errorresponse(response) } } catch (t: Throwable) { Resource.failure(t.message ?: "请求失败", null) } } // 按照上面的格式,补全callApiTwoSync、callApiThreeSync...所有你需要调用的接口 // 批量并行请求方法,等待所有请求完成后统一返回结果 fun batchCallAllApis(): List<Resource<*>> { val tasks = listOf( Callable { callApiOneSync() }, Callable { callApiTwoSync() }, Callable { callApiThreeSync() }, // 把剩下的其他接口任务也加进来 ) // 提交所有任务,阻塞到所有任务执行完成后返回结果列表 return apiExecutor.invokeAll(tasks).map { it.get() } } }
ViewModel层修改
class MyViewModel: ViewModel() { val allApiResultLiveData = MutableLiveData<List<Resource<*>>>() fun callAllApis() { // 切到IO线程执行请求,避免阻塞主线程 viewModelScope.launch(Dispatchers.IO) { val results = repository.batchCallAllApis() allApiResultLiveData.postValue(results) } } }
Fragment层修改
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewModel.callAllApis() viewModel.allApiResultLiveData.observe(this.viewLifecycleOwner) { resultList -> // 所有请求已完成,在这里统一处理所有接口的返回结果,按顺序对应你提交的任务 resultList.forEachIndexed { index, resource -> // 分别处理每个接口的成功/失败逻辑 } } }
方案2:更推荐的Kotlin协程实现
不需要自己维护线程池,Android官方原生支持,自动处理生命周期避免内存泄漏,代码更简洁:
首先先把你的Retrofit ApiService接口改成挂起函数:
interface ApiService { @GET("你的接口1路径") suspend fun callApiOne(): Response<Model> // 其他所有接口都加suspend关键字即可 }
Repository层修改
class Repository { // 每个接口改成挂起函数 suspend fun callApiOne(): Resource<Model> { return try { val response = AppMain.restClient?.services?.callApiOne() if (response?.isSuccessful == true) { Resource.successResp(response) } else { Resource.errorresponse(response) } } catch (t: Throwable) { Resource.failure(t.message ?: "请求失败", null) } } // 补全其他接口的挂起实现 // 并行请求方法 suspend fun batchCallAllApis(): List<Resource<*>> = coroutineScope { val deferredTasks = listOf( async { callApiOne() }, async { callApiTwo() }, async { callApiThree() }, // 加其他接口任务 ) // 等待所有请求完成返回结果 deferredTasks.awaitAll() } }
ViewModel和Fragment层的代码和方案1完全一致,不需要修改。
注意事项
- 如果你有部分接口不需要等其他请求完成就能单独渲染,可以单独写对应的LiveData返回结果,不需要都放到批量请求里
- 自定义的ExecutorService如果不在页面销毁时关闭,会有内存泄漏风险,用协程+viewModelScope的方案会在页面销毁时自动取消未完成的请求,更安全
内容的提问来源于stack exchange,提问作者hodokeg
相关产品推荐
相关产品推荐

