Android kotlin viewmodel factory

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

  1. Have Android studio installed.
  2. You must be familiar with Kotlin.
  3. Install lifecycle dependencies.
  4. 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 .

Читайте также:  Как очистить файловую систему андроид самсунг а10

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.

References

Peer Review Contributions by: Peter Kayere

About the author

A lover of technology. An upright individual not afraid of getting out of the comfort zone and trying out new things.

Want to learn more about the EngEd Program?

Discover Section’s community-generated pool of resources from the next generation of engineers.

Источник

ViewModel with ViewModelProvider.Factory : The Creator of ViewModel

Hi Friends! Today I’d like to describe how ViewModel use with ViewModelProvider.Factory.

Let’s Start

So the question, What is ViewModelProvider.Factory? Lets create our ViewModel implementation without ViewModelProvider.Factory.

And implementation in activity.

Did anyone notice that when we declare MyViewModel in activity using ViewModelProviders, We didn’t call any MyViewModel constructor. The ViewModelProviders internally manage for us and call our primary constructor of ViewModel and create the instance of ViewModel and give the instance back.

Now let see what happens when we pass argument to the constructor of MyViewModel.

Now create instance of MyViewModel in activity.

No compile time error! Yeah!!

When you run this code you will get RunTimeError, Boom. RuntimeException cannot create an instance of MyViewModel.

Now let’s see why this happen. The ViewModelProviders. of() method internally creates default ViewModelProvider.Factory implementation for creating our ViewModel with no argument. So when you add argument in the constructor, the inner implementation of ViewModelProvider.Factory failed to initialize your ViewModel because ViewModelProvider.Factory call the primary constructor for creating the ViewModel’s instance. Image below is the default implementation of ViewModelFactory.

If you add argument in constructor you have to create your own implementation of ViewModelProvider. Factory to create your ViewModel instance.

What is ViewModelProvider.Factory?

Implementations of ViewModelProviders.Factory interface are responsible to instantiate ViewModels. That means you write your own implementation for creating an instance of ViewModel.

Lets create our own implementation of ViewModelProvider.Factory as below:

Here is some points to remember.

  1. You pass your ViewModel arguments to the ViewModelProvider.Factory through constructor or any other pattern you prefer (Singleton, FactoryPattern etc.). And it is because you cannot call ViewModel constructor in Activity or Fragment when you initializing ViewModel and you want to set ViewModel constructor’s argument value so that’s why you need to pass argument value to ViewModelProvider.Factory and it will create your ViewModel.
  2. ViewModelProvider.Factory is an interface which have create method. The create method is responsible for creating our VeiwModel’s instance.
  3. The modelClass.getConstructor(Int::class.java) get the constructor which has type Int and create an instance of ViewModel by calling newInstance method and pass constructor values to this method.

Now, lets create our ViewModel instance using ViewModelProvider.Factory in our Activity.

We pass our arguments or dependencies to our ViewModelProvider.Factory so it will manage to create ViewModel for us. The ViewModelProviders.of( context, instanceOfViewModelFactory) method gets the instance of our ViewModelProvider.Factory and now we are responsible to our ViewModel instance process. Just like we do in this example.

So Why we need ViewModelProvider.Factory?

Here some questions comes on mind, I will not passing value to constructor of ViewModel, I will create method to set my values and it will work fine so why need this ViewModelProvider.Factory. In somehow you are right but some cases you need ViewModelProver.Factory.

When to use ViewModelProvider.Factory?

If your ViewModel have dependencies then you should pass this dependencies through the constructor (It is the best way to pass your dependencies), so you can mock that dependencies and test your ViewModel.

Источник

Урок 4. ViewModel

В этом уроке рассмотрим, как использовать ViewModel. Как сохранять данные при повороте экрана. Как передать Context в ViewModel. Как передать свои данные в конструктор модели с помощью фабрики. Как передать данные между фрагментами. Что использовать: ViewModel или onSavedInstanceState.

Полный список уроков курса:

ViewModel — класс, позволяющий Activity и фрагментам сохранять необходимые им объекты живыми при повороте экрана.

Давайте сразу посмотрим пример и по ходу дела разберем нюансы и прочую теорию. Как подключить библиотеку к проекту, вы можете посмотреть в начале Урока 2.

Создаем свой класс, наследующий ViewModel

Пока оставим его пустым.

Чтобы добраться до него в Activity, нужен следующий код:

В метод ViewModelProviders.of передаем Activity. Тем самым мы получим доступ к провайдеру, который хранит все ViewModel для этого Activity.

Методом )» target=»_blank» rel=»noopener noreferrer»>get запрашиваем у этого провайдера конкретную модель по имени класса — MyViewModel. Если провайдер еще не создавал такой объект ранее, то он его создает и возвращает нам. И пока Activity окончательно не будет закрыто, при всех последующих вызовах метода get мы будем получать этот же самый объект MyViewModel.

Соответственно, при поворотах экрана, Activity будет пересоздаваться, а объект MyViewModel будет спокойно себе жить в провайдере. И Activity после пересоздания сможет получить этот объект обратно и продолжить работу, как будто ничего не произошло.

Читайте также:  Android app in app purchase hack

Отсюда следует важный вывод. Не храните в ViewModel ссылки на Activity, фрагменты, View и пр. Это может привести к утечкам памяти.

На картинке время жизни (оно же scope) модели это выглядит так:

Модель жива, пока Activity не закроется окончательно.

У метода get, который возвращает нам модель из провайдера, есть еще такой вариант вызова:

Т.е. вы можете создавать несколько моделей одного и того же класса, но использовать разные текстовые ключи для их хранения в провайдере.

LiveData

LiveData очень удобно использовать с ViewModel. В прошлых уроках я для работы с LiveData использовал синглтон, но теперь мы перейдем на ViewModel.

Рассмотрим несложный пример асинхронной однократной загрузки каких-либо данных:

Основной метод здесь — это getData. Когда Activity захочет получить данные, оно вызовет именно этот метод. Мы проверяем, создан ли уже MutableLiveData. Если нет, значит этот метод вызывается первый раз. В этом случае создаем MutableLiveData и стартуем асинхронный процесс получения данных методом loadData. Далее возвращаем LiveData.

В методе loadData происходит асинхронное получение данных из какого-нибудь репозитория. Как только данные будут получены (в методе onLoad), мы передаем их в MutableLiveData.

Метод loadData должен быть асинхронным, потому что он вызывается из метода getData, а getData в свою очередь вызывается из Activity и все это происходит в UI потоке. Если loadData начнет грузить данные синхронно, то он заблокирует UI поток.

Код в Activity выглядит так:

Получаем от провайдера модель. От модели получаем LiveData, на который подписываемся и ждем данные.

В этом примере ViewModel нужен, чтобы сохранить процесс получения данных при повороте экрана. А LiveData — для удобного асинхронного получения данных.

Т.е. это будет выглядеть так:

— Activity вызывает метод модели getData
— модель создает MutableLiveData и стартует асинхронный процесс получения данных от репозитория
— Activity подписывается на полученный от модели LiveData и ждет данные
— происходит поворот экрана
— на модели этот поворот никак не сказывается, она спокойно сидит в провайдере и ждет ответ от репозитория
— Activity пересоздается, получает ту же самую модель от провайдера, получает тот же самый LiveData от модели и подписывается на него и ждет данные
— репозиторий возвращает данные, модель передает их в MutableLiveData
— Activity получает данные данные от LiveData

Если репозиторий вдруг пришлет ответ в тот момент, когда Activity будет пересоздаваться, то Activity получит этот ответ, как только подпишется на LiveData.

Если ваш репозиторий сам умеет возвращать LiveData, то все значительно упрощается. Вы просто отдаете этот LiveData в Activity и оно подписывается.

Очистка ресурсов

Когда Activity окончательно закрывается, провайдер удаляет ViewModel, предварительно вызвав его метод onCleared

В этом методе вы сможете выполнить все необходимые операции по освобождению ресурсов, закрытию соединений/потоков и т.п.

Context

Не стоит передавать Activity в модель в качестве Context. Это может привести к утечкам памяти.

Если вам в модели понадобился объект Context, то вы можете наследовать не ViewModel, а AndroidViewModel.

При создании этой модели, провайдер передаст ей в конструктор класс Application, который является Context. Вы сможете до него добраться методом getApplication.

Код получения этой модели в Activity останется тем же самым.

Передача объектов в конструктор модели

Бывает необходимость передать модели какие-либо данные при создании. Модель создается провайдером и у нас есть возможность вмешаться в этот процесс. Для этого используется фабрика. Мы учим эту фабрику создавать модель так, как нам нужно. И провайдер воспользуется этой фабрикой, когда ему понадобится создать объект.

Рассмотрим пример. У нас есть такая модель

Ей нужен long при создании.

В конструктор передаем long, который нам необходимо будет передать в модель.

В методе )» target=»_blank» rel=»noopener noreferrer»>create фабрика получит от провайдера на вход класс модели, которую необходимо создать. Проверяем, что это класс MyViewModel, сами создаем модель и передаем туда long.

В Activity код получения модели будет выглядеть так:

Мы создаем новую фабрику с нужными нам данными и передаем ее в метод of. При вызове метода get провайдер использует фабрику для создания модели, т.е. выполнится наш код создания модели и передачи в нее данных.

Передача данных между фрагментами

ViewModel может быть использована для передачи данных между фрагментами, которые находятся в одном Activity. В документации есть отличный пример кода:

SharedViewModel — модель с двумя методами: один позволяет поместить данные в LiveData, другой — позволяет получить этот LiveData. Соответственно, если два фрагмента будут иметь доступ к этой модели, то один сможет помещать данные в его LiveData, а другой — подпишется и будет получать эти данные. Таким образом два фрагмента будут обмениваться данными ничего не зная друг о друге.

Чтобы два фрагмента могли работать с одной и той же моделью, они могут использовать общее Activity. Код получения модели в фрагментах выглядит так:

Для обоих фрагментов getActivity вернет одно и то же Activity. Метод ViewModelProviders.of вернет провайдера этого Activity. Далее методом get получаем модель.

Фрагмент MasterFragment помещает данные в LiveData. А DetailFragment — подписывается и получает данные.

onSavedInstanceState

Чем ViewModel отличается от onSavedInstanceState. Для каких данных какой из них лучше использовать. Кажется, что если есть ViewModel, который жив все время, пока не закрыто Activity, то можно забыть про onSavedInstanceState. Но это не так.

Давайте в качестве примера рассмотрим Activity, которое отображает список каких-то данных и может выполнять поиск по ним. Пользователь открывает Activity и выполняет поиск. Activity отображает результаты этого поиска. Пользователь сворачивает приложение. Когда он его снова откроет, он ожидает, что там все останется в этом же состоянии.

Читайте также:  Капельная зарядка андроид что это такое

Но тут внезапно системе не хватает памяти и она убивает это свернутое приложение. Когда пользователь снова запустит его, Activity ничего не будет знать о поиске, и просто покажет все данные. В этом случае ViewModel нам никак не поможет, потому что модель будет убита вместе с приложением. А вот onSavedInstanceState будет выполнен. В нем мы сможем сохранить поисковый запрос, и при последующем запуске получить его из объекта savedInstanceState и выполнить поиск. В результате пользователь увидит тот же экран, который был, когда приложение было свернуто.

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

onSavedInstanceState — здесь нужно хранить тот минимум данных, который понадобится вам для восстановления состояния экрана и данных в ViewModel после экстренного закрытия Activity системой. Это может быть поисковый запрос, ID и т.п.

Соответственно, когда вы достаете данные из savedInstanceState и предлагаете их модели, это может быть в двух случаях:

1) Был обычный поворот экрана. В этом случае ваша модель должна понять, что ей эти данные не нужны, потому что при повороте экрана модель ничего не потеряла. И уж точно модель не должна заново делать запросы в БД, на сервер и т.п.

2) Приложение было убито, и теперь запущено заново. В этом случае модель берет данные из savedInstanceState и использует их, чтобы восстановить свои данные. Например, берет ID и идет в БД за полными данными.

RxJava

В начале этого урока мы рассмотрели пример работы ViewModel и LiveData. Возникает вопрос, можно ли заменить LiveData на Flowable?

У LiveData есть одно большое преимущество — он учитывает состояние Activity. Т.е. он не будет слать данные, если Activity свернуто. И он отпишет от себя Activity, которое закрывается.

А вот Flowable этого не умеет. Если в модели есть Flowable, и Activity подпишется на него, то этот Flowable будет держать Activity, пока оно само явно не отпишется (или пока Flowable не завершится).

Давайте рассмотрим пример. ViewModel обычно работает с репозиториями, которые могут быть синглтонами. В репозитории есть какой-то объект для подписки (типа LiveData или Flowable). Репозиторий периодически обновляет в нем данные. Модель берет этот объект из репозитория и отдает его в Activity, и Activity подписывается на этот объект. Объект теперь хранит ссылку на Activity.

Таким образом получилось, что репозиторий держит ссылку на Activity через объект подписки. И если мы закроем Activity, но не отпишем его от объекта подписки, то возникнет утечка памяти, т.к. репозиторий может жить все время работы приложения. И все это время Activity будет висеть в памяти.

Давайте рассмотрим, как это решается в случае с LiveData или Flowable. Важно понимать, что будет происходить с подпиской при закрытии Activity. ViewModel будем рассматривать только как инструмент передачи объекта из репозитория в Activity.

1) ViewModel готов из репозитория предоставить LiveData. И мы в Activiy хотели бы работать с LiveData.

Цепочка ссылок:
Repository -> LiveData -> Activity

Тут получается полная идилия. Activity берет из модели LiveData, подписывается на него и все ок. При закрытии Activity не будет никаких утечек памяти и прочих проблем с подпиской, т.к. LiveData сам отпишет Activity и тем самым разорвет цепочку ссылок.

2) ViewModel готов вернуть нам Flowable, а мы в Activity хотели бы работать с LiveData.

В этом случае конвертируем Flowable в LiveData внутри модели и отдаем LiveData в Activity.

Цепочка ссылок:
Repository -> Flowable -> LiveData -> Activity

Activity снова будет подписано на LiveData. А это значит, что нам, как и в первом варианте, не надо заботиться об этой подписке. LiveData отпишет от себя Activity, и сам отпишется от Flowable. Цепочка ссылок разорвется в двух местах.

3) ViewModel готов вернуть нам LiveData, а мы в Activity хотели бы работать с Flowable.

В этом случае передаем LiveData в Activity и преобразуем его в Flowable.

Цепочка ссылок:
Repository -> LiveData -> Flowable -> Activity

Activity будет подписано на Flowable. А Flowable будет подписан на LiveData. При этом подписка Flowable на LiveData будет работать с учетом Activity LifeCycle. И когда Activity будет закрыто, LiveData сам отпишет от себя Flowable.

Цепочка ссылок разорвется, но, в любом случае, хорошей практикой является отписка от Flowable вручную при закрытии Activity.

4) ViewModel готов вернуть нам Flowable, и мы в Activity хотели бы работать с Flowable.

В этом случае Activity будет подписано на Flowable.

Цепочка ссылок:
Repository -> Flowable -> Activity

При закрытии Activity нам самим необходимо отписать Activity от Flowable.

Как конвертировать LiveData во Flowable и наоборот, мы рассматривали в Уроке 3.

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Источник

Оцените статью