Котлин корутины. Часть 2. Управление пользовательским интерфейсом

Управление пользовательским интерфейсом через корутины

В этом упражнении мы напишем корутину для отображения сообщения после задержки. Для начала убедитесь, что у вас есть проект kotlin-coroutines-start, открытый в Android Studio. Скачать проект можно в предыдущей части.

Чтобы на практике увидеть работу с Kotlin Coroutines и архитектурными компонентами, записывайтесь на продвинутый курс по разработке приложения «Чат-мессенжер»

Добавьте область coroutine scope в MainViewModel

В Kotlin все корутины работают внутри CoroutineScope. Область действия контролирует время жизни корутин через свою работу. Когда вы отменяете работу области, она отменяет все корутины , запущенные в этой области. В Android вы можете использовать область действия для отмены всех запущенных корутин, когда, например, пользователь уходит от действия или фрагмента. Области также позволяют указать диспетчер по умолчанию. Диспетчер контролирует, какой поток запускает корутину.

Чтобы запустить корутины в MainViewModel.kt, область действия будет создана следующим образом:

private val viewModelJob = Job()

private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)

В нашем примере uiScope запустит корутины в Dispatchers.Main, которая является основным потоком в Android. Корутина, запущенная на главном, не будет блокировать главный поток, пока он приостановлен. Поскольку корутина ViewModel почти всегда обновляет пользовательский интерфейс в основном потоке, запуск корутин в основном потоке является разумным значением по умолчанию. Как мы увидим позже в этом коде, корутина может переключать диспетчеры в любое время после ее запуска. Например, корутина может запускаться в главном диспетчере, а затем использовать другой диспетчер для анализа большого JSON ресурса из основного потока.

CoroutineContext

CoroutineScope может принимать CoroutineContext как параметр. CoroutineContext это набор атрибутов, который настраивает корутину. Он может определять политику потоков, обработчик исключений и т. д.

В приведенном выше примере мы используем CoroutineContext плюс оператор для определения политики потоков (Dispatchers.Main) и работы (viewModelJob). Результирующий CoroutineContext это комбинация обоих контекстов.

Отмените область scope, когда ViewModel очищен

onCleared вызывается, когда ViewModel больше не используется и будет уничтожен. Обычно это происходит, когда пользователь уходит от активити или фрагмента, который использовал ViewModel. Если мы хотим отменить область действия, которую мы видели в предыдущем разделе, вам нужно будет включить следующий код.

override fun onCleared() {
    super.onCleared()
    viewModelJob.cancel()
}

Поскольку viewModelJob передается как задание в uiScope, при отмене viewModelJob все корутины, запущенные uiScope, также будут отменены. Важно отменить любые корутины, которые больше не требуются, чтобы избежать ненужной работы и утечек памяти.

Важно: Вы должны передать CoroutineScope Job для отмены всех корутин, запущенных в области. Если вы этого не сделаете, область будет работать, пока ваше приложение не будет завершено. Если это не то, что вы хотели, у вас будет утечка памяти.

Области, созданные с помощью конструктора CoroutineScope, добавляют неявное задание, которое можно отменить с помощью uiScope.coroutineContext.cancel ()

Используйте viewModelScope для избежания boilerplate code

Мы могли бы включить приведенный выше код в каждую ViewModel, имеющуюся в нашем проекте, чтобы привязать к нему область видимости. Тем не менее, это было бы много boilerplate кода. Вот почему мы используем библиотеку AndroidX lifecycle-viewmodel-ktx. Чтобы использовать эту библиотеку, вы должны включить ее в файл build.gradle (Module: app) вашего проекта. Этот шаг уже сделан в проекте, который вы скачали.

dependencies {
  ...
  implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:x.x.x"
}

Библиотека добавляет viewModelScope в качестве функции расширения класса ViewModel. Эта область привязана к Dispatchers.Main и будет автоматически отменена после очистки ViewModel. Вместо того, чтобы создавать новую область в каждой ViewModel, вы можете просто использовать viewModelScope, а библиотека позаботится о настройке и очистке области за вас.

Вот как вы можете использовать viewModelScope для запуска корутины, которая сделает сетевой запрос в фоновом потоке.

class MainViewModel : ViewModel() {
    // Make a network request without blocking the UI thread
    private fun makeNetworkRequest() {
       // launch a coroutine in viewModelScope 
        viewModelScope.launch(Dispatchers.IO) {
            // slowFetch()
        }
    }

    // No need to override onCleared()
}

Переключение с threads на coroutines

В MainViewModel.kt найдите следующий TODO вместе с этим кодом:

/**
* Wait one second then display a snackbar.
*/
fun onMainViewClicked() {
   // TODO: Replace with coroutine implementation
   BACKGROUND.submit {
       Thread.sleep(1_000)
       // use postValue since we're in a background thread
       _snackBar.postValue("Hello, from threads!")
   }
}

Этот код использует BACKGROUND для запуска в фоновом потоке. Поскольку sleep блокирует текущий поток, он заморозил бы пользовательский интерфейс, если бы он был вызван в основном потоке. Через одну секунду после того, как пользователь щелкает по main view, он запрашивает снэк-бар.

Замените onMainViewClicked на этот код на основе корутин, который делает то же самое. Вам придется импортировать запуск и задержку.

/**
* Wait one second then display a snackbar.
*/
fun onMainViewClicked() {
   // launch a coroutine in viewModelScope
   viewModelScope.launch {
       // suspend this coroutine for one second
       delay(1_000)
       // resume in the main dispatcher
       // _snackbar.value can be called directly from main thread
       _snackBar.value = "Hello, from coroutines!"
   }
}

Этот код делает то же самое, ожидая одну секунду, прежде чем показывать снэк-бар. Тем не менее, есть некоторые важные различия:

  1. viewModelScope.launch запустит корутину в viewModelScope. Это означает, что когда работа, которую мы передали viewModelScope, будет отменена, все корутины в этой работе / области будут отменены. Если пользователь покинул Activity перед возвратом задержки, эта корутина автоматически отменяется при вызове onCleared после уничтожения ViewModel.
  2. Поскольку viewModelScope имеет диспетчер по умолчанию Dispatchers.Main, эта корутина будет запущена в главном потоке. Позже мы увидим, как использовать разные потоки.
  3. Задержка функции является функцией приостановки. Это показано в Android Studio значком Котлин корутины. Часть 2. Управление пользовательским интерфейсом в левой панели. Даже если эта корутина выполняется в основном потоке, задержка не будет блокировать поток на одну секунду. Вместо этого диспетчер запланирует возобновление корутины через одну секунду при следующем вызове.

Запустите приложение. Когда вы щелкнете на главном экране, через секунду вы увидите снэк-бар.

Продолжение следует:

Котлин корутины. Часть 3. Тестирование корутин через поведение

Додати коментар