Как асинхронный запрос завершается
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Механизмы завершения асинхронных запросов в Android
Асинхронный запрос завершается через систему коллбэков, промисов или корутин, которые уведомляют приложение о результате операции без блокировки основного потока. Рассмотрим основные подходы, используемые в Android-разработке.
1. Коллбэки (Callbacks)
Наиболее традиционный способ — использование интерфейсов обратного вызова. Библиотеки типа OkHttp и Retrofit (до версии 2.6) широко применяли этот подход.
interface NetworkCallback {
fun onSuccess(response: String)
fun onFailure(error: Throwable)
}
fun fetchData(callback: NetworkCallback) {
thread {
try {
// Имитация сетевого запроса
Thread.sleep(1000)
callback.onSuccess("Данные получены")
} catch (e: Exception) {
callback.onFailure(e)
}
}
}
// Использование
fetchData(object : NetworkCallback {
override fun onSuccess(response: String) {
runOnUiThread { textView.text = response }
}
override fun onFailure(error: Throwable) {
runOnUiThread { showError(error.message) }
}
})
Проблемы коллбэков:
- Callback Hell — вложенные коллбэки ухудшают читаемость
- Сложность обработки ошибок
- Проблемы с отменой операций
2. Промисы/Future (RxJava, ListenableFuture)
Библиотеки RxJava и Guava предоставляют более структурированные подходы.
RxJava использует наблюдаемые последовательности:
apiService.getData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ data -> updateUI(data) },
{ error -> handleError(error) }
)
ListenableFuture из Guava:
val future: ListenableFuture<Response> = executor.submit(callable)
Futures.addCallback(future, object : FutureCallback<Response> {
override fun onSuccess(result: Response) {
runOnUiThread { processResult(result) }
}
override fun onFailure(t: Throwable) {
runOnUiThread { handleFailure(t) }
}
}, executor)
3. Корутины (Coroutines) — современный подход
В Kotlin корутины предоставляют наиболее элегантное решение через механизм приостановки (suspension) без блокировки потоков.
// Suspend-функция в репозитории
suspend fun fetchUserData(): User {
return withContext(Dispatchers.IO) {
// Запрос к сети
apiService.getUser()
}
}
// Использование в ViewModel
viewModelScope.launch {
try {
val user = repository.fetchUserData()
_userData.value = user
} catch (e: Exception) {
_errorMessage.value = e.message
}
}
Ключевые преимущества корутин:
- Структурная конкурентность — операции можно легко отменять через Scope
- Последовательный код — асинхронные операции выглядят как синхронные
- Интеграция с жизненным циклом через
viewModelScope,lifecycleScope
4. WorkManager для фоновых задач
Для отложенных или периодических задач, которые должны выполняться гарантированно:
val workRequest = OneTimeWorkRequestBuilder<NetworkWorker>()
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.build()
WorkManager.getInstance(context).enqueue(workRequest)
5. Механизм завершения под капотом
При завершении асинхронного запроса происходят следующие этапы:
- Получение ответа — сетевая библиотека получает данные от сервера
- Парсинг данных — преобразование JSON/XML в объекты модели
- Передача результата — через коллбэк, эмиттер RxJava или возобновление корутины
- Обработка в основном потоке — обновление UI через
runOnUiThread,Handler, или диспетчеры
Важные аспекты завершения:
- Обработка ошибок — обязательная проверка сетевых сбоев, таймаутов
- Отмена операций — при уничтожении Activity/Fragment
- Сохранение состояния — обработка поворотов экрана и конфигурационных изменений
- Кэширование — сохранение результатов для оффлайн-работы
Пример комплексного решения с корутинами и Retrofit:
class UserRepository(private val api: UserApi, private val dao: UserDao) {
suspend fun getUserWithCache(userId: String): User {
// Сначала проверяем кэш
val cachedUser = withContext(Dispatchers.IO) { dao.getUser(userId) }
if (cachedUser != null) return cachedUser
// Если нет в кэше — сетевой запрос
return try {
val networkUser = api.getUser(userId)
withContext(Dispatchers.IO) { dao.insertUser(networkUser) }
networkUser
} catch (e: Exception) {
// Логика повторных попыток или возврат ошибки
throw UserFetchException("Не удалось загрузить пользователя", e)
}
}
}
В современных Android-приложениях корутины стали стандартом для асинхронных операций благодаря своей безопасности, читаемости и глубокой интеграции с экосистемой Kotlin и Android Jetpack.