- Implementing MVVM architecture in Android using Kotlin
- Introduction
- Prerequisites
- The goal of the tutorial
- Step 1 – Launching Android Studio
- Step 2 – Creating the model
- Step 3 – Creating the view
- Step 4 – Creating the item_view
- Step 5 – Create a RecyclerView Adapter
- Step 6 – Creating the ViewModel
- Step 7 – Create the ViewModel Factory
- Step 8 – MainActivity (connecting the code)
- Step 9 – Results
- Conclusion
- Современная Android разработка на Kotlin. Часть 2
- Архитектура MVVM + Паттерн Repository + Android Manager Wrappers
- Пару слов об Архитектуре в мире Андроид
- Что такое MVVM паттерн?
- Пример кода
- MVVM на Android с компонентами архитектуры + библиотека Koin
- Представление
- Модель представления
- Модель
- Как реализовать паттерн MVVM
- Сценарий приложения и реализация модели
- View-Model
- Представление
- Конкретизация объектов и внедрение зависимостей
- Заключение
Implementing MVVM architecture in Android using Kotlin
November 17, 2020
This tutorial is suitable for beginners. Especially those who have just started learning Android programming in Kotlin. Every application needs to follow certain architectural principles.
Failure to adhere to this requirement results in applications difficult to scale and maintain. As a result, more time and resources will be needed to push even simple updates. Therefore, the developer may end up missing crucial opportunities.
Introduction
Let us start by evaluating what android architectures existed before MVVM. The first component is Model View Presenter denoted by MVP. Though this architecture separates the business logic from the app’s UI, it is difficult to implement.
In the long-run, this can translate into high development costs. The second android architecture is MVC.
Just like MVP, it is also quite complex and not suitable for minor projects. Google introduced MVVM (Model-View-ViewModel) to resolve these challenges. By separating code into smaller chunks, MVVM simplifies the debugging process.
Through this article, you’ll understand MVVM architecture and implement it in an application. This article shows how to debug common errors resulting from this architecture.
Learn more about MVVM here.
Prerequisites
- Have Android studio installed.
- You must be familiar with Kotlin.
- Install lifecycle dependencies.
- Download the start code from here.
The goal of the tutorial
By the end of this tutorial, you will create an app that takes input and displays it on a recycler view. Below is the screenshot of the app.
Step 1 – Launching Android Studio
Launch Android Studio and create a new project, as shown below. Make sure that you select Kotlin as your preferred programming language.
If you don’t have Android Studio, you can install it from here.
Step 2 – Creating the model
Create the app model. Also referred to as the data class. To avoid confusion, create a package named model inside the java folder. Then, create a data class named Blog in the model package, as shown below.
For simplicity, the data class will only have one variable (title). There is no need to add getters and setters; Kotlin adds them to the class automatically.
Here’s the code for the class.
Step 3 – Creating the view
The view is what the user sees on the screen. The UI, therefore, needs to be well structured to minimize any confusion and dissatisfaction.
Open activity_main.xml file and change the Layout from constraint to linear Layout. Set the orientation to vertical; this arranges the UI components vertically in the Layout. Our app’s primary widgets are Edittext , Button , and a RecyclerView .
Make sure all these widgets have IDs since they will be vital at a later stage. This is how our activity_main.xml file should look like.
Step 4 – Creating the item_view
Still on the Layout, we need to create the design of the element shown in the RecyclerView . Therefore, create a file named item.xml and add the code shown in the image below. The design is simple since the user can also access one attribute from the data class.
Step 5 – Create a RecyclerView Adapter
A RecyclerView adapter handles the binding of the item.xml layout to the RecyclerView . It also takes in a list of items and displays them to the user. The code for the RecyclerView adapter is shown below.
Step 6 – Creating the ViewModel
Create a package named ViewModel . Inside this folder, create a Kotlin class and name it MainViewModel . The class should extend the Android ViewModel . You might face an error if you failed to add lifecycle dependencies from Jetpack.
The MainViewModel will have a mutable livedata item that holds the array list. It’s vital to use LiveData since it notifies the UI in case of any data change. The MainViewModel code is shown below.
Step 7 – Create the ViewModel Factory
The purpose of a ViewModel factory is to instantiate the ViewModel . This prevents the app from crashing in case an activity is not found.
The code for our MainViewModelFactory is shown below.
Step 8 – MainActivity (connecting the code)
We have created the model, ViewModel , ViewModelfactory , and RecyclerView . These components need to be instantiated in the MainActivity class for the application to work.
Start by declaring the RecyclerView and instantiating it. Set the layout manager for the RecyclerView to LinearLayoutManager . The MainActivity file contains three major methods; initialiseAdapter , observeData , and addData . the initialiseAdapter method assigns a ViewManager to the RecyclerView .
The observeData function looks for changes in the viewmodel and forwards them to the RecyclerAdapter . The addData method takes in the user’s input and updates the list in the ViewModel .
Step 9 – Results
If there were no errors in your code, it should compile and show the UI in the image below. Whatever you type in the EditText field should display in the recyclerview once you click the submit button.
Conclusion
MVVM architecture has made it easier to build complex applications. As shown, it’s easier to identify bugs due to the separation of business logic from the UI code. The architecture also prevents data loss during configuration changes. Ensure that all dependencies are present before using MVVM. This measure helps prevent runtime errors.
Современная Android разработка на Kotlin. Часть 2
Привет, Хабр! Представляю вашему вниманию перевод статьи «Modern Android development with Kotlin (Part 2)» автора Mladen Rakonjac.
Примечание. Данная статья является переводом циклов статей от Mladen Rakonjac, дата статьи: 23.09.2017. GitHub. Начав читать первую часть от SemperPeritus обнаружил, что остальные части почему-то не были переведены. Поэтому и предлагаю вашему вниманию вторую часть. Статья получилась объёмной.
«Очень сложно найти один проект, который охватывал бы всё новое в разработке под Android в Android Studio 3.0, поэтому я решил написать его.»
В этой статье мы разберём следующее:
- Android Studio 3, beta 1 Часть 1
- Язык программирования Kotlin Часть 1
- Варианты сборки Часть 1
- ConstraintLayout Часть 1
- Библиотека привязки данных Data Binding Часть 1
- Архитектура MVVM + Паттерн Repository + Android Manager Wrappers
- RxJava2 и как это помогает нам в архитектуре Part 3
- Dagger 2.11, что такое внедрение зависимости, почему вы должны использовать это Part 4
- Retrofit (with Rx Java2)
- Room (with Rx Java2)
Архитектура MVVM + Паттерн Repository + Android Manager Wrappers
Пару слов об Архитектуре в мире Андроид
Довольно долгое время андроид-разработчики не использовали какую-либо архитектуру в своих проектах. В последние три года вокруг неё в сообществе андроид-разработчиков поднялось много шумихи. Время God Activity прошло и Google опубликовал репозиторий Android Architecture Blueprints, с множеством примеров и инструкций по различным архитектурным подходам. Наконец, на Google IO ’17 они представили Android Architecture Components — коллекцию библиотек, призванных помочь нам создавать более чистый код и улучшить приложения. Component говорит, что вы можете использовать их все, или только один из них. Впрочем, я нашёл их все реально полезными. Далее в тексте и в следующих частях мы будет их использовать. Сперва я в коде доберусь до проблемы, после чего проведу рефакторинг, используя эти компоненты и библиотеки, чтобы увидеть, какие проблемы они призваны решить.
Существуют два основных архитектурных паттерна, которые разделяют GUI-код:
- MVP
- MVVM
Трудно сказать, что лучше. Вы должны попробовать оба и решить. Я предпочитаю MVVM, используя lifecycle-aware компоненты и я напишу об этом. Если вы никогда не пробовали использовать MVP, на Medium есть куча хороших статей об этом.
Что такое MVVM паттерн?
MVVM — это архитектурный паттерн, раскрывается как Model-View-ViewModel. Я думаю это название смущает разработчиков. Если бы я был тем, кто придумал ему имя, я бы назвал его View-ViewModel-Model, потому что ViewModel находится посередине, соединяя View и Model.
View — это абстракция для Activity, Fragment‘а или любой другой кастомной View (Android Custom View). Обратите внимание, важно не путать эту View с Android View. View должна быть тупой, мы не должны писать какую-либо логику в неё. View не должна содержать данные. Она должна хранить ссылку на экземпляр ViewModel и все данные, которые нужны View, должны приходить оттуда. Кроме того, View должна наблюдать за этими данными и layout должен поменяться, когда данные из ViewModel изменятся. Подводя итог, View отвечает за следующее: вид layout’а для различных данных и состояний.
ViewModel — это абстрактное имя для класса, содержащего данные и логику, когда эти данные должны быть получены и когда показаны. ViewModel хранит текущее состояние. Также ViewModel хранит ссылку на одну или несколько Model‘ей и все данные получает от них. Она не должна знать, к примеру, откуда получены данные, из базы данных или с сервера. Кроме того, ViewModel не должна ничего знать о View. Более того, ViewModel вообще ничего не должна знать о фреймворке Android.
Model — это абстрактное имя для слоя, который подготавливает данные для ViewModel. Это класс, в котором мы будем получать данные с сервера и кэшировать их, или сохранять в локальную базу данных. Заметьте, что это не те же классы, что и User, Car, Square, другие классы моделей, которые просто хранят данные. Как правило, это реализация шаблона Repository, который мы рассмотрим далее. Model не должна ничего знать о ViewModel.
MVVM, если реализован правильно, это отличный способ разбить ваш код и сделать его более тестируемым. Это помогает нам следовать принципам SOLID, поэтому наш код легче поддерживать.
Пример кода
Сейчас я напишу простейший пример, показывающий как это работает.
Для начала, давайте создадим простенькую Model, которая возвращает некую строчку:
Обычно получение данных — это асинхронный вызов, поэтому мы должны ждать его. Чтобы сымитировать это, я поменял класс на следующий:
Я создал интерфейс OnDataReadyCallback с методом onDataReady . И теперь метод refreshData реализует (имплементирует) OnDataReadyCallback . Для имитации ожидания я использую Handler . Раз в 2 секунды метод onDataReady будет вызываться у классов, реализующих интерфейс OnDataReadyCallback .
Давайте создадим ViewModel:
Как вы можете видеть, здесь есть экземпляр RepoModel , text , который будет показан и переменная isLoading , которая хранит текущее состояние. Давайте создадим метод refresh , отвечающий за получение данных:
Метод refresh вызывает refreshData у RepoModel , который в аргументах берёт реализацию OnDataReadyCallback . Хорошо, но что такое object ? Всякий раз, когда вы хотите реализовать (implement) какой-либо интерфейс или унаследовать (extend) какой-либо класс без создания подкласса, вы будете использовать объявление объекта (object declaration). А если вы захотите использовать это как анонимный класс? В этом случае вы используете object expression:
Когда мы вызываем refresh , мы должны изменить view на состояние загрузки и когда данные придут, установить у isLoading значение false .
Также мы должны заменить text на
Обратите внимание, что я использую val вместо var, потому что мы изменим только значение в поле, но не само поле. И если вы захотите проинициализировать его, используйте следующее:
Давайте изменим наш layout, чтобы он мог наблюдать за text и isLoading. Для начала, привяжем MainViewModel вместо Repository:
- Изменим TextView для наблюдения за text из MainViewModel
- Добавим ProgressBar, который будет виден только если isLoading true
- Добавим Button, которая при клике будет вызывать метод refresh из MainViewModel и будет кликабельна только в случае isLoading false
Если вы сейчас запустите, то получите ошибку View.VISIBLE and View.GONE cannot be used if View is not imported . Что ж, давайте импортируем:
Хорошо, с макетом закончили. Теперь закончим со связыванием. Как я сказал, View должна иметь экземпляр ViewModel :
MVVM на Android с компонентами архитектуры + библиотека Koin
С MVVM ( Model— View-ViewModel) процесс разработки графического интерфейса для пользователей делится на две части. Первая — это работа с языком разметки или кодом GUI. Вторая — разработка бизнес-логики или логики бэкенда (модель данных). Часть V iew model в MVVM — это конвертер значений. Это значит, что view model отвечает за конвертирование объектов данных из модели в такой вид, чтобы с объектами было легко работать. Если смотреть с этой стороны, то view model — это скорее модель, чем представление. Она контролирует большую часть логики отображения. Модель представления может реализовывать паттерн медиатор. Для этого организуется доступ к логике бэкенда вокруг набора юз-кейсов, поддерживаемых представлением.
В этом туториале мы попробуем определить каждый компонент паттерна MVVM, чтобы создать небольшое приложение на Android в соответствии с ним.
На следующей картинке — разные элементы, которые мы собираемся создать при помощи компонента Architecture и библиотеки Koin для внедрения зависимостей.
Архитектуру ниже можно разделить на три различные части.
Представление
Содержит структурное определение того, что пользователи получат на экранах. Вы можете поместить сюда статическое и динамическое содержимое (анимацию и смену состояний). Тут может не быть никакой логики приложения. Для нашего случая в представлении может быть активность или фрагмент.
Модель представления
Этот компонент связывает модель и представление. Отвечает за управление ссылками данных и возможных конверсий. Здесь появляется биндинг. В Android мы не беспокоимся об этом, потому что можно напрямую использовать класс AndroidViewModel или ViewModel.
Модель
Это уровень бизнес-данных и он не связан ни с каким особенным графическим представлением. В Android, согласно “чистой” архитектуре, модель может содержать базу данных, репозиторий и класс бизнес-логики. Картинка ниже описывает взаимодействие между разными компонентами.
Как реализовать паттерн MVVM
Чтобы реализовать паттерн MVVM, важно начать с компонентов, которым для работы нужен другой компонент. Это и есть зависимость.
А с момента появления компонента архитектуры, логичное общее решение — реализовать Android-приложения при помощи модели с изображения ниже. Там вы увидите стрелки, которые ведут от представления (активности/фрагмента) к модели.
А это значит, что View знает о View-Model, а не наоборот, и View Model знает о Model, и не наоборот. То есть у представления будет связь с моделью представления, а у модели представления будет связь с моделью. Строго в таком порядке, никак иначе. Благодаря такой архитектуре приложение легко поддерживать и тестировать.
Чтобы программировать быстро и эффективно, вам нужно начать с моделирования, так как модели не нужны другие компоненты для работы.
Сценарий приложения и реализация модели
Чтобы понять, как функционирует паттерн MVVM, мы напишем небольшое приложение, в котором будут все компоненты с предыдущей картинки. Мы создадим программу, которая покажет данные. Мы их взяли по этой ссылке. Приложение будет сохранять данные локально для того, чтобы потом оно работало в режиме оффлайн.
Приложение будет обрабатывать данные такой структуры. А для простоты я выберу всего лишь некоторые параметры. У класса GithubUser есть room-аннотация и у данных в локальной БД будет такая же структура, как и у данных в API.
У пространства DAO есть только два метода. Один — добавление информации в БД. Второй — ее извлечение.
Пространство базы данных выглядит так:
Во второй части мы реализуем Webservice, который отвечает за получение данных онлайн. Для того будем пользоваться retrofit+coroutines.
Если вы хотите узнать, как пользоваться Retrofit вместе с сопрограммами, загляните сюда .
В третьей части мы реализуем репозиторий. Этот класс будет отвечать за определение источника данных. Для нашего случая их два, так что репозиторий будет только получать данные онлайн, чтобы потом сохранить их в локальной базе данных.
Как сами видите, у репозитория есть конструктор с двумя параметрами. Первый — это класс, который представляет онлайн-данные, а второй — представляет данные оффлайн.
View-Model
После того, как мы описали модель и все ее части, пора ее реализовать. Для этого возьмем класс, родителем которого является класс ViewModel Android Jetpack.
Класс ViewModel создан для того, чтобы хранить и управлять данными, связанными с UI относительно жизненного цикла. Он позволяет данным пережить изменения конфигурации, например, повороты экрана.
View-model берет репозиторий в качестве параметра. Этот класс “знает” все источники данных для нашего приложения. В начальном блоке view-model мы обновляем данные БД. Это делается вызовом метода обновления репозитория. А еще у view-model есть свойство data. Оно получает данные локально напрямую. Это гарантия, что у пользователя всегда будет что-то в интерфейсе, даже если устройство не в сети.
Подсказка: я пользовался вспомогательным классом, который помогал мне управлять состоянием загрузки
Представление
Это последний компонент архитектуры. Он напрямую общается с представлением-моделью, получает данные и, например, передает их в recycler-view. В нашем случае представление — это простая активность.
В представлении происходит отслеживание того, как изменяются данные, как они автоматически обновляются на уровне интерфейса. Для нашего случая в представлении также отслеживается состояние операций загрузки в фоновом режиме. В процесс включено свойство loadingState, которое мы определили выше.
Вот вы и увидели, как я получил экземпляр view-model, используя для этого внедрение. А как это сработает, мы увидим дальше.
Конкретизация объектов и внедрение зависимостей
Наблюдательные заметят, что пока я еще не создал репозиторий и его параметры. Мы будет это делать точно при помощи внедрения зависимостей. А для этого в свою очередь мы берем библиотеку, Koin подходит идеально.
Так мы создадим важные объекты. Нашему приложению они нужны там же и нам останется только вызвать их в разные точки программы. Для этого и нужна магия библиотеки Koin.
В Module.kt есть объявление объекта, который нужен приложению. А в представлении мы берем inject, который говорит Koin, что нужен объект view-model. Библиотека в свою очередь старается найти этот объект в модуле, который мы определили ранее. Когда найдёт, назначит ему свойство userViewModel. А если не найдёт, то выдаст исключение. В нашем случае, код скомпилируется правильно, у нас есть экземпляр view-model в модуле с соответствующим параметром.
Похожий сценарий применится к репозиторию внутри view-model. Экземпляр будет получен из модуля Koin, потому что мы уже создали репозиторий с нужным параметром внутри модуля Koin.
Заключение
Самая сложная работа инженера ПО — это не разработка, а поддержка. Чем больше кода имеет под собой хорошую архитектуру, тем проще поддерживать и тестировать приложение. Вот почему важно пользоваться паттернами. С ними проще создать стабильно работающие программы, а не бомбу.
Вы можете найти полный код приложения у меня на GitHub по этой ссылке.