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

Какие знаешь виды скоупов в корутинах?

2.0 Middle🔥 171 комментариев
#Жизненный цикл и навигация#Многопоточность и асинхронность

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

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

Какие знаешь виды скоупов в корутинах

Коротутины в Kotlin используют coroutine scopes для управления жизненным циклом асинхронных операций. Это критически важно для правильного управления памятью и отмены операций.

1. GlobalScope (Глобальный скоп)

Коротутина живет столько же, сколько приложение. Это отличный пример антипаттерна.

GlobalScope.launch {
    val data = apiService.fetchData()
}

Почему это плохо:

  • Невозможно отменить операцию
  • Memory leak при закрытии Activity
  • Могут возникнуть UnhandledJobException
  • Сложно тестировать

2. ViewModelScope (для MVVM)

Привязан к жизненному циклу ViewModel. Это рекомендуемый подход для Android.

class UserViewModel : ViewModel() {
    fun refreshUser() {
        viewModelScope.launch {
            try {
                val user = userRepository.getUser(1)
            } catch (e: Exception) {
                handleError(e)
            }
        }
    }
}

Преимущества:

  • Автоматическая отмена при destroy ViewModel
  • Привязан к жизненному циклу
  • Встроена обработка ошибок

3. LifecycleScope (для Activity/Fragment)

Привязан к жизненному циклу Activity или Fragment.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycleScope.launch {
            val data = fetchData()
            updateUI(data)
        }
    }
}

Состояния Lifecycle:

  • CREATED
  • STARTED
  • RESUMED

4. Custom Scope (Пользовательский скоп)

Для полного контроля жизненного цикла корутин.

class CustomRepository {
    private val scope = CoroutineScope(Job() + Dispatchers.Main)
    
    fun loadData(onResult: (String) -> Unit) {
        scope.launch {
            val data = fetchDataFromApi()
            onResult(data)
        }
    }
    
    fun cancel() {
        scope.cancel()
    }
}

5. Dispatchers (Выполнение контекст)

Диспетчеры определяют на каком потоке выполняется корутина.

withContext(Dispatchers.Main) {
    updateUI()
}

val data = withContext(Dispatchers.IO) {
    database.getUser(1)
}

val result = withContext(Dispatchers.Default) {
    expensiveCalculation(data)
}

Рекомендуемые диспетчеры:

  • Dispatchers.Main — UI операции
  • Dispatchers.IO — сеть, БД, файлы
  • Dispatchers.Default — CPU-bound операции

6. SupervisorJob (для иерархии)

Для обработки ошибок без отмены других корутин.

val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

scope.launch {
    throw Exception()
}

scope.launch {
    println("This WILL print")
}

Это важно когда ошибка в одной корутине не должна отменить другие.

7. Scope с обработкой ошибок

val exceptionHandler = CoroutineExceptionHandler { _, exception ->
    handleError(exception)
}

val scope = CoroutineScope(
    Job() + Dispatchers.Main + exceptionHandler
)

Экспешны автоматически обрабатываются для всех корутин.

Сравнение скопов

СкопЖизненный циклКогда использоватьОтмена
GlobalScopeприложениеТолько тестыНикогда
ViewModelScopeViewModelMVVMАвтоматична
LifecycleScopeActivity/FragmentUI операцииАвтоматична
CustomРучнаяПолный контрольРучная
SupervisorJobИерархияНезависимые задачиНезависимая

Лучшие практики

Запрещено:

  • Никогда не используй GlobalScope в production коде

Рекомендуется:

  • ViewModelScope для UI-related операций
  • LifecycleScope для Fragment/Activity
  • SupervisorJob для независимых задач
  • Всегда обрабатывай исключения
  • Правильно выбирай Dispatcher
  • Отменяй корутины в onDestroy()

Выбор правильного скопа критичен для избежания memory leaks и правильного управления асинхронными операциями в Android.