← Назад к вопросам

Как асинхронный запрос завершается

1.8 Middle🔥 162 комментариев
#Многопоточность и асинхронность

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Механизмы завершения асинхронных запросов в 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. Механизм завершения под капотом

При завершении асинхронного запроса происходят следующие этапы:

  1. Получение ответа — сетевая библиотека получает данные от сервера
  2. Парсинг данных — преобразование JSON/XML в объекты модели
  3. Передача результата — через коллбэк, эмиттер RxJava или возобновление корутины
  4. Обработка в основном потоке — обновление 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.