Как дождаться дооснащения.getData() полные сопрограммы Kotlin

#android #api #kotlin #retrofit #kotlin-coroutines

Вопрос:

Я получаю данные из API, и мне нужно подождать, пока работа не будет завершена. Из-за изменчивых данных. Когда он работает, response.issuscesful=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
}
 

Комментарии:

1. Вы уже используете viewModel , Почему бы не использовать a LiveData для наблюдения за результатом . Также не создавайте Retrofit instace для каждого вызова, создавайте его как Синглтон и используйте его везде

2. Я наблюдаю, но через 2 секунды все меняется. Мне нужно установить значение ошибки, если ответ не будет успешным. Но это не удается, через 2 секунды это превращается в успешное

Ответ №1:

Прежде всего, вы должны убедиться getData() , что он определен как функция приостановки, чтобы вам не пришлось возиться с этими withContext оболочками. Они вам не нужны при вызове правильно написанных функций приостановки, потому что правильные функции приостановки не блокируются, и они делегируются соответствующему диспетчеру внутренне, если им нужен конкретный.

Тогда у вас есть два варианта.

  1. Вместо того, чтобы возвращать что-то из функции, опубликуйте результат в LiveData (или SharedFlow/StateFlow). Внешний класс может вызвать эту функцию и наблюдать за живыми данными, чтобы работать с результатом.
 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 блока. Таким образом, внешнему классу нужно только наблюдать за живыми данными, и если пользователь поворачивает устройство, он не перезапускает запрос каждый раз и не задерживает загрузку данных.

Ответ №2:

Если у вас getData() есть suspend функция, вы должны быть в порядке, потому что по умолчанию код внутри сопрограмм является последовательным, что означает, что ничто ниже val response = retrofit.getData() не может выполняться до getData() возврата.

Если это не так, просто сделайте это suspend функцией, потому что Retrofit поддерживает ее изначально.

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

Что касается вопроса, вы можете позвонить job.join() , чтобы дождаться завершения работы, но в вашем случае это вам совсем не поможет. И join() является функцией приостановки, поэтому она должна вызываться либо из другой suspend функции, либо из другой сопрограммы.