Сопрограммы Kotlin & Anko: возвращение вне асинхронного
В нашем проекте нам нужно получить некоторые данные (которые будут сохранены) перед выполнением операций. Если данные были получены более 15 минут назад, мы должны обновить их.
Для этого мы используем сопрограммы Kotlin + Anko. Идея (при условии, что данные были получены ранее) заключается в том, что:
Метод вызывается и проверяет, когда мы получили данные. Если это было менее 15 минут назад, возвращает его. Если нет, получите его асинхронно (это сетевая операция), сохраните его и верните.
Поскольку мы ничего не можем сделать до получения обновленных данных, обновление должно быть синхронным (в течение всей сетевой операции асинхронно).
У нас есть этот код:
fun Context.retrieveInfo(api: Api?): User? {
try {
// For sake of simplification the real conditional check is removed
if (time > 15) {
val context = this
val asyncUser = async(UI) {
val obtainData: Deferred<Data?> = bg {
api?.obtainData(sphelper.getDefaultUser(context))
}
val obtainedData = storeAndRetrieve(obtainData.await(), context)
return@async obtainedData
}
// ???????
} else {
val input = ObjectInputStream(this.openFileInput("data.dat"))
return input.readObject() as Data
}
} catch (e: Exception) {
return null
}
}
Как мы можем заставить функцию ждать результата за пределами асинхронного (UI) блока? Это возвращение необходимо, но мы не имеем ни малейшего представления о том, что мы должны там поставить. Мы пытались с помощью метода getCompleted() объекта Deferred (возвращаем asyncUser.getCompleted()), но в итоге происходит сбой, потому что он возвращает ноль.
Спасибо!
1 ответ
Я вижу пару способов сделать это. Одним из них является использование kotlinx.coroutines.experimental
метод runBlocking
:
val user = runBlocking(CommonPool) {
val asyncUser = async(UI) {
val obtainData: Deferred<String> = bg {
...
}
val obtainedData = storeAndRetrieve(obtainData.await(), context)
return@async obtainedData
}
return@runBlocking asyncUser.await()
}
В зависимости от варианта использования вы можете даже упростить это до:
val asyncUser = runBlocking {
async(CommonPool) {
val obtainedData = storeAndRetrieve(api?.obtainData(sphelper.getDefaultUser(context)), context)
return@async obtainedData
}.await()
}
Другой способ заключается в использовании базовой Java CountDownLatch
:
var asyncUser: String? = null
val c = CountDownLatch(1)
async(UI) {
val obtainData: Deferred<String> = bg {
...
}
val obtainedData = obtainData.await()
asyncUser = obtainedData
c.countDown()
}
c.await()