fbpx

Создание ViewModel

ViewModel является частью lifecycle library и предоставляет данные для UI и умеет переживать изменения конфигурации. ViewModel является посредником между репозиторием и UI.  Кроме того, вы можете использовать ViewModel для обмена данными между фрагментами.

 

Когда использовать ViewModel?

ViewModel содержит данные для UI и умеет корректно обрабатывать изменения конфигурации, то есть переживать пересоздание Activity/Fragment. Отдельное от Activity/Fragment хранение  данных, который нужно отображать в UI позволяет следовать принципу единственной ответственности (single responsibility principle) Таким образом, пока Activity или Fragment нужны только для создания экрана, ViewModel в свою очередь занимается наполнением и обработкой данных, отображаемых в UI – элементах. Лучше всего использовать ViewModel совместно с LiveData для данных, которые могут изменится после отображения на экране. Использование LiveData имеет несколько преимуществ:

  • Можно подписаться на обновление данных (вместо постоянной проверки на изменения вручную). Тогда UI обновится только в том случае, когда это необходимо, то есть когда изменятся данные.
  • Репозиторий (как источник данных) и UI полностью разделены и общаются через ViewModel.
  • Не нужно делать запросы в БД из ViewModel (эта логика реализуется в репозитории). Это делает код более тестируемым.

viewModelScope

В Kotlin все корутины запускаются в так называемом CoroutineScope Скоупы контролиуруют время жизни корутин. Когда вы отменяете задачу в скоупе, то отменяются все корутины запущенные в этом скоупе. Библиотека lifecycle-viewmodel-ktx являющаяся частью AndroidX имеет расширениеviewModelScope позволяющее работать со скоупами.

Создание ViewModel

Создадите класс WordViewModel как показано ниже

// Class extends AndroidViewModel and requires application as a parameter.
class WordViewModel(application: Application) : AndroidViewModel(application) {

    // The ViewModel maintains a reference to the repository to get data.
    private val repository: WordRepository
    // LiveData gives us updated words when they change.
    val allWords: LiveData<List<Word>>

    init {
        // Gets reference to WordDao from WordRoomDatabase to construct
        // the correct WordRepository. 
        val wordsDao = WordRoomDatabase.getDatabase(application).wordDao()
        repository = WordRepository(wordsDao)
        allWords = repository.allWords
    }

    /**
     * The implementation of insert() in the database is completely hidden from the UI.
     * Room ensures that you're not doing any long running operations on 
     * the main thread, blocking the UI, so we don't need to handle changing Dispatchers.
     * ViewModels have a coroutine scope based on their lifecycle called 
     * viewModelScope which we can use here.
     */
    fun insert(word: Word) = viewModelScope.launch {
        repository.insert(word)
    }
}

Как обычно, давайте разберём, что здесь есть:

  1. Класс WordViewModel принимает Application в качестве аргумента и наследуется от  AndroidViewModel.
  2. val repository является приватным свойством для хранения ссылки на репозиторий.
  3. Для кэширования списка слов добавим публичное свойство val allWords: LiveData<List<Word>>
  4. init блок инициализирует переменнуб  wordDao являющейся экземпляром WordDao из  WordRoomDatabase.
  5. В init блоке инициализируется через репозиторий переменная LiveData   allWords
  6. Метод insert() является методом – обёрткой для вызова метода insert() в репозитории. Так как работать с БД рекомендуется в отдельном потоке мы используем корутины для вызова метода в отдельном потоке, на случай если вставка элемента затянется, то мы не будем блокировать главный поток, а значит для пользователя работа приложения будет максимально удобной.

В следующих уроках мы создадим UI для отображения данных на экране приложения.