【问题标题】:How to wait for retrofit.getData() complete Kotlin Coroutines如何等待 retrofit.getData() 完成 Kotlin Coroutines
【发布时间】:2021-10-15 20:21:15
【问题描述】:

我正在从 API 获取数据,我需要等到作业完成。因为 MutableLiveData。 当它工作 response.isSusccesful=false 时,它​​会变为 true。我如何等到 retrofit.getData() 完成。我尝试了运行阻塞,但这不是合适的方法。我尝试了 async{} 和 await() 但我也没有成功。

fun loadData():ArrayList<CryptoData> {

    val retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()
        .create(CryptoAPI::class.java)

    job = viewModelScope.launch {
        withContext(Dispatchers.IO){
            val response = retrofit.getData()

            withContext(Dispatchers.Main){

                if (response.isSuccessful){
                    response.body()?.let {
                        cryptoDatas = ArrayList(it)
                        currency.value = cryptoDatas
                        progressBarStatus.value = false
                        println(response.isSuccessful)

                    }
                }
                else{
                    println(response.isSuccessful)
                }
            }
        }
    }

    return cryptoDatas
}

【问题讨论】:

  • 您已经在使用viewModel 为什么不使用LiveData 来观察结果。也不要为每个调用创建 Retrofit 实例,将其创建为 Singlton 并在其他任何地方使用它
  • 我正在观察,但它在 2 秒后发生了变化。如果响应不成功,我需要设置错误值。但2秒后不成功,转为成功

标签: android api kotlin retrofit kotlin-coroutines


【解决方案1】:

首先,您应该确保将getData() 定义为挂起函数,这样您就不必与那些withContext 包装器混淆。调用正确编写的挂起函数时不需要它们,因为适当的挂起函数不会阻塞,如果需要特定的调度器,它们会在内部委托给适当的调度程序。

那么你有两个选择。

  1. 不要从函数返回某些内容,而是将结果发布到 LiveData(或 SharedFlow/StateFlow)。外部类可以调用此函数并观察 LiveData 以处理结果。
private val _cryptoData = MutableLiveData<ArrayList<CryptoData>>()
val cryptoData: LiveData<ArrayList<CryptoData>> = _cryptoData

fun loadData() {

    val retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()
        .create(CryptoAPI::class.java)

    viewModelScope.launch {
        val response = retrofit.getData()
        if (response.isSuccessful){
            response.body()?.let {
                cryptoData.value = ArrayList(it)
                currency.value = it
                progressBarStatus.value = false
                println(response.isSuccessful)
            } 
        else {
            println(response.isSuccessful)
        }
    }
}
  1. 使loadData() 成为挂起函数。您的外部类可以使用协程调用它,然后按顺序对结果执行某些操作。
suspend fun loadData(): List<CryptoData>? {

    val retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()
        .create(CryptoAPI::class.java)

    val response = retrofit.getData()
    return if (response.isSuccessful){
        response.body()?.let {
            currency.value = it
            progressBarStatus.value = false
            println(response.isSuccessful)
            ArrayList(it)
        }
    } else {
        println(response.isSuccessful)
        null
    }
}

但我在字里行间阅读并猜测这是您每次显示此片段或活动时加载的数据。因此,您应该使用选项 1,除非您将此函数设为私有并从 init 块调用它。这样,外部类只需要观察LiveData,如果用户旋转设备,它不会每次都重新请求并延迟数据加载完成的时间。

【讨论】:

    【解决方案2】:

    如果你的 getData() 是一个 suspend 函数,你应该一切都好,因为默认情况下,协程内的代码是顺序的,这意味着在 val response = retrofit.getData() 以下的任何内容都不能运行,直到 getData() 返回。

    如果它不仅仅是一个 suspend 函数,因为 Retrofit 本身就支持它。

    @GET("...")
    suspend fun getData(): Response<...>
    

    关于这个问题,您可以致电job.join() 等待工作完成,但在您的情况下,这对您毫无帮助。而join() 是一个挂起函数,因此必须从另一个suspend 函数或另一个协程调用它。

    【讨论】:

      猜你喜欢
      • 2020-09-02
      • 2019-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-10
      • 2022-11-11
      • 2021-10-27
      • 2020-12-14
      相关资源
      最近更新 更多