You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Retrofit2并发请求异常:RecyclerView无法正常填充数据

排查与解决思路

这种并发请求导致其中一个UI组件不更新的问题我之前也碰到过,大概率是这几个原因之一,咱们一步步来排查和解决:

1. 检查Retrofit Call实例是否被复用

Retrofit的Call对象是一次性的,如果你在两个方法里复用了同一个Call实例,同时执行的话第二个请求会直接失败。确保每次调用fetchPremiumStudios()和fetchDistricts()时,都是创建新的Call对象:

错误示例(复用Call实例):

// 错误:同一个Call不能被多次执行
private val premiumStudiosCall = apiService.getPremiumStudios()

fun fetchPremiumStudios() {
    premiumStudiosCall.enqueue(...)
}

正确写法(每次新建Call):

fun fetchPremiumStudios() {
    val call = apiService.getPremiumStudios() // 每次调用都创建新的Call实例
    call.enqueue(object : Callback<List<Studio>> {
        override fun onResponse(call: Call<List<Studio>>, response: Response<List<Studio>>) {
            // 处理数据并更新RecyclerView
        }

        override fun onFailure(call: Call<List<Studio>>, t: Throwable) {
            // 一定要打印错误信息!这里很容易忽略
            t.printStackTrace()
        }
    })
}

2. 确保RecyclerView的更新在主线程执行

虽然Retrofit默认的Callback是在主线程回调,但如果你自定义了Retrofit的Executor,可能会导致回调在子线程执行。此时必须切换到主线程更新UI:

override fun onResponse(call: Call<List<Studio>>, response: Response<List<Studio>>) {
    if (response.isSuccessful) {
        val studios = response.body() ?: emptyList()
        // 强制在主线程更新RecyclerView
        requireActivity().runOnUiThread {
            yourAdapter.submitList(studios)
            // 如果用的是普通Adapter,记得调用notifyDataSetChanged()
            yourAdapter.notifyDataSetChanged()
        }
    }
}

3. 检查是否有回调冲突或数据被覆盖

仔细查看fetchDistricts()的回调逻辑,有没有不小心操作到RecyclerView的Adapter——比如误重置了Adapter实例、清空了数据:

// 错误示例:fetchDistricts里误操作了RecyclerView的Adapter
fun fetchDistricts() {
    apiService.getDistricts().enqueue(object : Callback<List<District>> {
        override fun onResponse(call: Call<List<District>>, response: Response<List<District>>) {
            // ...填充Spinner的逻辑
            // 不小心写了这句,直接覆盖了RecyclerView的Adapter
            recyclerView.adapter = someOtherAdapter
        }
    })
}

确保两个请求的回调只操作各自对应的UI组件,不要交叉影响。

4. 确认fetchPremiumStudios是否请求失败

同时调用时,这个请求可能因为网络超时、接口返回错误等原因失败,但你没处理onFailure回调。一定要在onFailure里打印异常日志:

override fun onFailure(call: Call<List<Studio>>, t: Throwable) {
    Log.e("PremiumStudios", "请求失败: ${t.message}", t)
    // 也可以在这里给用户显示错误提示
}

通过Logcat查看错误信息,就能知道为什么数据为空了。

5. 检查RecyclerView的初始化时机

确保在调用fetchPremiumStudios()之前,RecyclerView和它的Adapter已经完成初始化。比如在onViewCreated()里先初始化UI,再发起请求:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    // 先初始化RecyclerView和Adapter
    val adapter = PremiumStudiosAdapter()
    recyclerView.adapter = adapter
    recyclerView.layoutManager = LinearLayoutManager(context)
    
    // 再同时发起两个请求
    fetchDistricts()
    fetchPremiumStudios()
}

6. 推荐:用Coroutines替代Callback(更简洁可靠)

如果你的项目已经用了Kotlin协程,改用协程处理并发请求会避免很多回调冲突的问题,代码也更易读:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // 初始化RecyclerView...
    
    lifecycleScope.launch {
        // 并发执行两个请求
        val districtsDeferred = async { apiService.getDistricts() }
        val studiosDeferred = async { apiService.getPremiumStudios() }
        
        try {
            val districts = districtsDeferred.await()
            val studios = studiosDeferred.await()
            
            // 更新Spinner
            districtSpinner.adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, districts)
            
            // 更新RecyclerView
            yourAdapter.submitList(studios)
        } catch (e: Exception) {
            Log.e("MainFragment", "请求失败: ${e.message}", e)
        }
    }
}

内容的提问来源于stack exchange,提问作者Ganesh T

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 02:32:27