#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
, Почему бы не использовать aLiveData
для наблюдения за результатом . Также не создавайтеRetrofit
instace для каждого вызова, создавайте его как Синглтон и используйте его везде2. Я наблюдаю, но через 2 секунды все меняется. Мне нужно установить значение ошибки, если ответ не будет успешным. Но это не удается, через 2 секунды это превращается в успешное
Ответ №1:
Прежде всего, вы должны убедиться getData()
, что он определен как функция приостановки, чтобы вам не пришлось возиться с этими withContext
оболочками. Они вам не нужны при вызове правильно написанных функций приостановки, потому что правильные функции приостановки не блокируются, и они делегируются соответствующему диспетчеру внутренне, если им нужен конкретный.
Тогда у вас есть два варианта.
- Вместо того, чтобы возвращать что-то из функции, опубликуйте результат в 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)
}
}
}
- Сделайте
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
функции, либо из другой сопрограммы.