Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работают корутины в Kotlin
Корутины — это один из самых мощных инструментов в Kotlin для асинхронного программирования. Это упрощение работы с асинхронным кодом по сравнению с callbacks и RxJava.
Что такое корутина?
Корутина — это функция, которая может быть приостановлена и возобновлена без блокировки потока. В отличие от потоков, корутины:
- Легче по памяти (можно создать тысячи корутин вместо сотен потоков)
- Не блокируют поток при ожидании результата
- Легче управлять и отладить
Основные концепции
1. Suspend-функции
Suspend-функция — это функция, которая может быть приостановлена и возобновлена.
suspend fun fetchUser(id: String): User {
// Эта функция может быть приостановлена
return api.getUser(id) // Сетевой запрос
}
Обычная функция не может вызвать suspend-функцию. Нужен контекст корутины:
viewModelScope.launch {
val user = fetchUser("123") // OK, мы внутри корутины
}
2. Dispatcher (Диспетчер)
Dispatcher определяет, на каком потоке будет выполняться корутина:
launch(Dispatchers.Main) {
// Запустится на главном потоке UI
}
launch(Dispatchers.IO) {
// Запустится на потоке для I/O операций (сеть, БД)
val user = fetchUser("123")
}
launch(Dispatchers.Default) {
// Запустится на фоновом потоке (CPU-bound операции)
val result = heavyComputation()
}
Структурированность корутин
Важный принцип — корутины в Kotlin структурированы, т.е. дочерние корутины не могут жить дольше родительской.
viewModelScope.launch { // Родительская корутина
launch { // Дочерняя корутина 1
val user = fetchUser("123")
}
launch { // Дочерняя корутина 2
val posts = fetchPosts("123")
}
// Корутина viewModelScope не завершится, пока обе дочерние не завершатся
}
Это гарантирует, что:
- Нет утечек памяти (дочерние корутины не висят в памяти)
- При отмене родителя отменяются все дети
- Исключения в дочерних корутинах пропагируются к родителю
Практический пример: загрузка пользователя
class UserViewModel(private val api: UserApi) : ViewModel() {
private val _user = MutableLiveData<User>()
val user: LiveData<User> = _user
private val _error = MutableLiveData<String>()
val error: LiveData<String> = _error
fun loadUser(id: String) {
viewModelScope.launch {
try {
val user = withContext(Dispatchers.IO) {
api.getUser(id) // Запрос на IO потоке
}
_user.value = user // Обновление на Main потоке
} catch (e: Exception) {
_error.value = e.message
}
}
}
}
viewModelScope
viewModelScope — это встроенная CoroutineScope для ViewModel, которая:
- Создаётся вместе с ViewModel
- Отменяется при уничтожении ViewModel
- Запускается на Dispatchers.Main.immediate по умолчанию
async/await для параллельных операций
Когда нужно выполнить несколько операций параллельно:
viewModelScope.launch {
try {
val userDeferred = async(Dispatchers.IO) { api.getUser("123") }
val postsDeferred = async(Dispatchers.IO) { api.getPosts("123") }
val user = userDeferred.await() // Ждём результат
val posts = postsDeferred.await()
_data.value = Pair(user, posts)
} catch (e: Exception) {
_error.value = e.message
}
}
Обработка исключений
viewModelScope.launch {
try {
val result = fetchData()
} catch (e: CancellationException) {
// Корутина была отменена (нормально)
throw e // Обязательно пробросить дальше
} catch (e: Exception) {
// Произошла ошибка
_error.value = e.message
}
}
Ключевые преимущества
- Простой синтаксис — код выглядит как синхронный
- Отмена операций — легко отменить все корутины при уничтожении View
- Управление ошибками — try/catch вместо сложных callback-цепочек
- Эффективность — тысячи корутин вместо потоков
- Интеграция — встроена в Jetpack компоненты (ViewModel, WorkManager и т.д.)