Android mvvm live data

MVVM Architecture & LiveData, ViewModel, LifeCycle Components

Jan 3, 2018 · 8 min read

MVC, MVP and MVVM are some of the Architecture patterns used by developers around the world. Which one to use, depends upon the requirement and how comfortable is one with the architecture pattern. However you should try to use one, it helps to keep the code segregated, modularised, readable, decoupled and easier to test.

Since the android development started Google never came up with their own architecture until recently. Google has introduced android architecture components which includes Life cycles, ViewModel, Room and LiveData.

Why am I here

This Article will provide brief of MVVM Architecture, LiveData, ViewModel and Lifecycle components.

Let’s get started

Befo r e we start, I would recommend you to download a project from here. This Application displays list of news in a Recycler view and on click of any news item, detailed news will be opened in a WebView. You can also refer screenshots from here. As we go through the article, we will be using pieces of code from this project to understand architecture pattern and components.

Downloaded, Good to Go 👍🏻

So Finally there is an architecture(MVVM) which follows the Single Responsibility Principle. Each component does single and unique Job, which is why it is recommended to write as many component classes as many needed. Before moving to MVVM Let’s understand Android components.

We will start with LiveData

LiveData is an observable holder class. It is like any other observer which implements Observer class, but LiveData is also linked with component (activity/Fragment/Service) life cycle. In case a component is destroyed, it will automatically stop observing the changes and release unused allocated memory which would help in avoiding memory leaks.

Using LiveData you consolidate your UI since observer would be triggering the UI changes if there is any change in data properties. We can wrap around an object to LiveData and start listening for data changes.

In the Above Example News object is being fetched by an api call and is wrapped inside LiveData. UI layer observes LiveData object changes. So on change of object properties, UI would be changed automatically. Also, it will save us the trouble of handling cases such as what happens if user kills activity in the middle of API call since it will stop listening to the changes as soon as activity is destroyed.

ViewModel

If you use a ViewModel, you would not have to worry about saving the data on configuration changes. In general, on configuration change activity is recreated and so does every other data retrieval. It can be an API call, db call or any other data related operation. ViewModel saves us all this trouble and keep the data alive until component is killed.

Let’s say we have an activity which displays result based upon searched query. Activity gets search query text from another component and then makes an API call to display results. In this case if user rotates device, there would be repetitive API call. If a ViewModel is used for any such cases then data would be saved automatically. ViewModel objects are automatically retained on configuration changes.

Читайте также:  Чем открывать файлы doc для андроид

ViewModel objects are attached to component life cycle and only gets away when the component is destroyed. In case of Activity it would be in onDestroy() and in case of Fragment it would be in onDetach().

In the example, News object is wrapped inside NewsViewModel and same is used for observing data inside fragment.

Also, You should avoid keeping references of views in ViewModel , that might lead memory leaks since ViewModel does not get destroyed on configuration changes. However ViewModel won’t be retained on back press, if application is removed from recent or if system kills the activity. In such cases onSaveInstance method has to be implemented to retain the needed data.

Apart from this You can share data between two fragments using ViewModel. It can be done something like this.

Off course, You can do it using interfaces but that is a cumbersome way to handle communication. If you use ViewModel, Activity and Fragment would not have any clue of communication. They will be communicating through SharedViewModel. You can read more about it from here.

Handling Life Cycle

This Architecture components comes with LifeCycle Observer. As name suggests you can create an observer which will bind to a component’s lifecycle.

In the below example SomeObserver is implementing LifecycleObserver class. SomeObserver is bound to an activity so it will follow activity’s lifecycle. As Activity’s onResume gets called, onResume of SomeObserver will also be called since it is linked to ON_RESUME event of Activity.

Use case: You want to fetch user’s fine location only if activity is visible. if not then coarse location. For this, In the observer class for onResume and onPause method you can switch the location update source from coarse to fine or vice-versa.

Fragments and Activities already implement the LifeCycleOwner interface in Support Library 26.1.0 and later. Before that you would have to extend LifeCycleFragment or LifeCycleActivity for fragment and activity correspondingly.

Also, You can make your own class Lifecycle Owner by implementing LifeCycleOwner class and mark the methods which you want to expose using LifecycleRegistry.

We are done with Architecture components. Let’s talk about MVVM Architecture now.

Model-View-ViewModel

DataModel

DataModel contains the data from different sources, can be API or can be from database . It is the Single entry point to your data. Most of the times datamodel contains API or DataBase Logic. DataModel fetches the data from required sources and present it to ViewModel. This layer also handles business logic so that latest and required data is exposed to the ViewModel.

ViewModel

This ViewModel is different from what we discussed above. This is term defined for MVVM Architecture.

ViewModel interacts with Data Model by an observable and It exposes only needed data for View Layer by an observable. Basically ViewModel does the same job for View, what Model does for ViewModel.

ViewModel prepares data for UI layer and keep it if needed, Any action on UI will be handled by ViewModel and any data changes will be observed by UI in View Layer. This helps to keep the UI and data Logic separated.

ViewModel exposes only relevant and needed data to the View. If we need 2 properties of an object, we will fetch those 2 properties, combine them into single object and then expose for View.

Читайте также:  Прошивка планшета андроид с компьютера

View is the UI interface , It can be an activity, fragment or a custom view. ViewModel prepares data for View, Binding of ViewModel to View is done in layout XML This is called Data binding of Layout Files.

To make the XML bindable, layout tag needs to be included in the XML. By Default a Binding class would be generated which will have references of all views and properties. if name of layout is activity_main, binding file would be ActivityMainBinding.

In layout XML file we can also include data object, with the help of which data can be populated to the views.

In the above example we have declared Article object and we are using this object to set the title in textview. Expressions within the layout are written in the attribute properties using the “ @<> » syntax.

However some times we need to write custom implementation for a property, Let’s say we are getting a Date as String from data object and want to format this date to another format, For such cases we can write our own custom methods annotating with BindingAdapter and passing the property name.

In the example i am using dateText property. Android does not know this property so we have to write a method which represents this property, now anywhere in the application you can use this tag and on run time it will call the BindingAdapter annotated method.

You can pass multiple properties for BindingAdapter method, making them optional or mandatory. You can read more about binding from here.

Test Project

Now let’s try to put all these pieces together and find out what is what in the sample project.

Источник

Android Architecture Components. Часть 3. LiveData

Компонент LiveData — предназначен для хранения объекта и разрешает подписаться на его изменения. Ключевой особенностью является то, что компонент осведомлен о жизненном цикле и позволяет не беспокоится о том, на каком этапе сейчас находиться подписчик, в случае уничтожения подписчика, компонент отпишет его от себя. Для того, чтобы LiveData учитывала жизненный цикл используется компонент Lifecycle, но также есть возможность использовать без привязки к жизненному циклу.

Сам компонент состоит из классов: LiveData, MutableLiveData, MediatorLiveData, LiveDataReactiveStreams, Transformations и интерфейса: Observer.

Класс LiveData, являет собой абстрактный дженериковый класс и инкапсулирует всю логику работы компонента. Соответственно для создания нашего LiveData холдера, необходимо наследовать этот класс, в качестве типизации указать тип, который мы планируем в нем хранить, а также описать логику обновления хранимого объекта.

Для обновления значения мы должны передать его с помощью метода setValue(T), будьте внимательны поскольку этот метод нужно вызывать с main треда, в противном случае мы получим IllegalStateException, если же нам нужно передать значение из другого потока можно использовать postValue(T), этот метод в свою очередь обновит значение в main треде. Интересной особенностью postValue(T) является еще то, что он в случае множественного вызова, не будет создавать очередь вызовов на main тред, а при исполнении кода в main треде возьмет последнее полученное им значение. Также, в классе присутствует два колбека:

onActive() — будет вызван когда количество подписчиков изменит свое значение с 0 на 1.
onInactive() — будет вызван когда количество подписчиков изменит свое значение с 1 на 0.

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

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

Читайте также:  Root права андроид fly

В целом логика фрагмента следующая, если кто-то подписывается, мы инициализируем BroadcastReceiver, который будет нас уведомлять об изменении сети, после того как отписывается последний подписчик мы перестаем отслеживать изменения сети.

Для того чтобы добавить подписчика есть два метода: observe(LifecycleOwner, Observer ) — для добавления подписчика с учетом жизненного цикла и observeForever(Observer ) — без учета. Уведомления об изменении данных приходят с помощью реализации интерфейса Observer, который имеет один метод onChanged(T).

Выглядит это приблизительно так:

Примечание: Этот фрагмент только для примера, не используйте этот код в реальном проекте. Для работы с LiveData лучше использовать ViewModel(про этот компонент в следующей статье) или позаботиться про отписку обсервера вручную.
В случае использования observe(this,this) при повороте экрана мы будем каждый раз отписываться от нашего компонента и заново подписываться. А в случае использование observeForever(this) мы получим memory leak.

Помимо вышеупомянутых методов в api LiveData также входит getValue(), hasActiveObservers(), hasObservers(), removeObserver(Observer observer), removeObservers(LifecycleOwner owner) в дополнительных комментариях не нуждаются.

Класс MutableLiveData, является расширением LiveData, с отличием в том что это не абстрактный класс и методы setValue(T) и postValue(T) выведены в api, то есть публичные.
По факту класс является хелпером для тех случаев когда мы не хотим помещать логику обновления значения в LiveData, а лишь хотим использовать его как Holder.

Класс MediatorLiveData, как понятно из названия это реализация паттерна медиатор, на всякий случай напомню: поведенческий паттерн, определяет объект, инкапсулирующий способ взаимодействия множества объектов, избавляя их от необходимости явно ссылаться друг на друга. Сам же класс расширяет MutableLiveData и добавляет к его API два метода: addSource(LiveData , Observer ) и removeSource(LiveData ). Принцип работы с классом заключается в том что мы не подписываемся на конкретный источник, а на наш MediatorLiveData, а источники добавляем с помощью addSource(..). MediatorLiveData в свою очередь сам управляет подпиской на источники.

Для примера создадим еще один класс LiveData, который будет хранить название нашей мобильной сети:

И перепишем наше приложение так чтоб оно отображало название wifi сети, а если подключения к wifi нет, тогда название мобильной сети, для этого изменим MainActivity:

Как мы можем заметить, теперь наш UI подписан на MediatorLiveData и абстрагирован от конкретного источника данных. Стоит обратить внимание на то что значения в нашем медиаторе не зависят напрямую от источников и устанавливать его нужно в ручную.

Класс LiveDataReactiveStreams, название ввело меня в заблуждение поначалу, подумал что это расширение LiveData с помощью RX, по факту же, класс являет собой адаптер с двумя static методами: fromPublisher(Publisher publisher), который возвращает объект LiveData и toPublisher(LifecycleOwner lifecycle, LiveData liveData), который возвращает объект Publisher . Для использования этого класса, его нужно импортировать отдельно:
compile «android.arch.lifecycle:reactivestreams:$version»

Класс Transformations, являет собой хелпер для смены типизации LiveData, имеет два static метода:
map(LiveData , Function ) — применяет в main треде реализацию интерфейса Function и возвращает объект LiveData

, где T — это типизация входящей LiveData, а P желаемая типизация исходящей, по факту же каждый раз когда будет происходить изменения в входящей LiveData она будет нотифицировать нашу исходящую, а та в свою очередь будет нотифицировать подписчиков после того как переконвертирует тип с помощью нашей реализации Function. Весь этот механизм работает за счет того что по факту исходящая LiveData, является MediatiorLiveData.

switchMap(LiveData , Function ) — похож к методу map с отличием в том, что вместо смены типа в функции мы возвращаем сформированный объект LiveData.

Базовый пример можно посмотреть в репозитории: git

Источник

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