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

如何让RecyclerView等待Retrofit请求完成后再初始化?

正确解决RecyclerView等待Retrofit请求完成的方案

你的问题核心在于当前的getStaff()函数虽然标记为suspend,但内部用了Retrofit的enqueue()异步回调,导致协程根本不会等待请求完成就继续执行后续代码,所以你才需要用delay()这种不靠谱的方式来“凑时间”。下面给你两种合理的解决方案,从基础修复到更符合MVVM架构的优化:

方案一:修复协程与Retrofit的配合(基础版)

首先要改造ViewModel里的getStaff(),让它真正利用协程的挂起特性等待请求完成:

1. 改造Retrofit接口

确保你的API接口定义为suspend函数(Retrofit 2.6.0+支持这个特性):

interface HarryPotterApi {
    @GET("api/characters/staff") // 替换成你实际的接口路径
    suspend fun staff(): List<YourCharacterModel> // 直接返回数据实体,而非Call对象
}

2. 重写ViewModel的getStaff()

去掉enqueue()回调,直接在协程中调用suspend接口方法,这样函数会挂起直到请求完成:

// 建议在ViewModel中使用viewModelScope,避免内存泄漏
fun getStaff() = viewModelScope.launch {
    val retrofit = Retrofit.Builder()
        .baseUrl("http://hp-api.herokuapp.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build()
    val service = retrofit.create(HarryPotterApi::class.java)
    try {
        val characterData = service.staff()
        if (characterData.isNotEmpty()) {
            // 清空旧数据再添加新数据,避免重复
            pictureList.clear()
            actorList.clear()
            characterList.clear()
            houseList.clear()
            
            pictureList.add(characterData[0].name)
            actorList.add(characterData[0].name)
            characterList.add(characterData[0].name)
            houseList.add(characterData[0].name)
        }
    } catch (e: IOException) {
        // 处理网络连接错误
        e.printStackTrace()
    } catch (e: HttpException) {
        // 处理HTTP状态码错误(比如404、500)
        e.printStackTrace()
    }
}

3. 改造Activity中的协程

使用和Activity生命周期绑定的lifecycleScope(替代GlobalScope,更安全),直接调用getStaff()后初始化RecyclerView:

lifecycleScope.launch {
    val txt = findViewById<TextView>(R.id.textbox) 
    viewModel.getStaff() // 这里会自动挂起,直到请求完成
    // 请求完成后自动回到主线程,直接初始化RV
    Log.d("coroutineScope", "#runs on ${Thread.currentThread().name}") 
    createRV(viewModel.pictureList, viewModel.actorList, viewModel.characterList, viewModel.houseList) 
}

方案二:用LiveData实现MVVM架构(推荐版)

上面的基础版虽然能解决问题,但UI和数据的耦合还是有点高。更优雅的方式是让ViewModel用LiveData持有数据,Activity观察LiveData的变化,自动触发RecyclerView的初始化:

1. ViewModel中添加LiveData

// 先定义一个数据类封装所需的列表数据
data class StaffData(
    val pictureList: List<String>,
    val actorList: List<String>,
    val characterList: List<String>,
    val houseList: List<String>
)

class YourViewModel : ViewModel() {
    // 私有可变LiveData,只在内部更新
    private val _staffLiveData = MutableLiveData<StaffData>()
    // 对外暴露不可变的LiveData,供UI观察
    val staffLiveData: LiveData<StaffData> = _staffLiveData

    fun getStaff() = viewModelScope.launch {
        val retrofit = Retrofit.Builder()
            .baseUrl("http://hp-api.herokuapp.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
        val service = retrofit.create(HarryPotterApi::class.java)
        try {
            val characterData = service.staff()
            if (characterData.isNotEmpty()) {
                val data = StaffData(
                    pictureList = listOf(characterData[0].name),
                    actorList = listOf(characterData[0].name),
                    characterList = listOf(characterData[0].name),
                    houseList = listOf(characterData[0].name)
                )
                // 用postValue确保在主线程更新LiveData
                _staffLiveData.postValue(data)
            }
        } catch (e: IOException) {
            e.printStackTrace()
        } catch (e: HttpException) {
            e.printStackTrace()
        }
    }
}

2. Activity中观察LiveData

在onCreate()中观察LiveData,数据更新时自动初始化RecyclerView:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.your_activity_layout)

    val viewModel = ViewModelProvider(this)[YourViewModel::class.java]
    
    // 观察数据变化,自动触发RV初始化
    viewModel.staffLiveData.observe(this) { staffData ->
        createRV(staffData.pictureList, staffData.actorList, staffData.characterList, staffData.houseList)
    }

    // 触发请求
    lifecycleScope.launch {
        viewModel.getStaff()
    }
}

关键注意点

  • 永远不要用GlobalScope:它的生命周期不受Activity/ViewModel控制,容易造成内存泄漏,改用lifecycleScope(Activity)或viewModelScope(ViewModel)。
  • Retrofit版本:确保你的Retrofit依赖是2.6.0及以上,不然不支持suspend接口。
  • 异常处理:不要忽略网络请求的异常,至少要打印日志方便排查问题。
  • 数据清空:每次请求前清空旧数据,避免列表重复添加内容。

内容的提问来源于stack exchange,提问作者zain mahmood

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 21:42:29