- Android Architecture Components: LiveData
- 1. The LiveData Component
- 2. Observing LiveData
- 3. Implementing LiveData
- Example Implementation
- 4. The MutableLiveData Helper Class
- 5. Transformations of LiveData
- Map Transformations
- SwitchMap Transformations
- 6. The MediatorLiveData
- 7. Conclusion
- Урок 2. LiveData
- Подключение библиотеки
- Теория
- Получение данных из LiveData
- Нюансы поведения
- Отправка данных в LiveData
Android Architecture Components: LiveData
We’ve already covered a lot of ground in our Android Architecture Components series. We started out talking about the idea behind the new architecture and looking at the key components presented at Google I/O. In the second post, we began our deep exploration of the main components of the package, taking a close look at the Lifecycle and LiveModel components. In this post, we’ll continue to explore the Architecture Components, this time analyzing the awesome LiveData component.
I’m assuming that you’re familiar with the concepts and components covered in the last tutorials, like Lifecycle , LifecycleOwner , and LifecycleObserver . If you’re not, take a look at the first post in this series, where I discuss the general idea behind the new Android Architecture and its components.
We’ll continue to build on the sample application we started in the last part of this series. You can find it in the tutorial GitHub repo.
1. The LiveData Component
LiveData is a data holder. It is capable of being observed, it can hold any kind of data, and on top of that, it is also lifecycle-aware. In practical terms, LiveData can be configured to only send data updates when its observer is active. Thanks to its lifecycle awareness, when observed by a LifecycleOwner the LiveData component will send updates only when the observer’s Lifecycle is still active, and it will remove the observed relation once the observer’s Lifecycle is destroyed.
The LiveData component has many interesting characteristics:
- prevents memory leaks when the observer is bound to a Lifecycle
- prevents crashes due to stopped activities
- data is always up to date
- handles configuration changes smoothly
- makes it possible to share resources
- automatically handles lifecycle
2. Observing LiveData
A LiveData component sends data updates only when its observer is «active». When observed by a LifecycleOwner , the LiveData component considers the observer to be active only while its Lifecycle is on the states STARTED or RESUMED , otherwise it will regard the observer as inactive. During the observer’s inactive state, LiveData will stop the data update stream, until its observer becomes active once again. If the observer is destroyed, LiveData will remove its reference to the observer.
To achieve this behavior, LiveData creates a tight relation with the observer’s Lifecycle when observed by a LifecycleOwner . This capacity makes it easier to avoid memory leaks when observing LiveData . However, if the observing process is called without a LifecycleOwner , the LiveData component won’t react to Lifecycle state, and the observer’s status must be handled manually.
To observe a LiveData , call observe(LifecycleOwner, Observer ) or observeForever(Observer ) .
- observe(LifecycleOwner, Observer ) : This is the standard way to observe a LiveData . It ties the observer to a Lifecycle , changing LiveData ‘s active and inactive state according to the LifecycleOwner current state.
- observeForever(Observer ) : This method doesn’t use a LifecycleOwner , so the LiveData won’t be able to respond to Lifecycle events. When using this method, it is very important to call removeObserver(Observer ) , otherwise the observer may not be garbage collected, causing a memory leak.
3. Implementing LiveData
The generic type in the LiveData class defines the type of data that will be held. For example, LiveData holds Location data. Or LiveData holds a String .
There are two main methods that must be considered in the component implementation: onActive() and onInactive() . Both methods react to the observer’s state.
Example Implementation
In our sample project we used lots of LiveData objects, but we only implemented one: LocationLiveData . The class deals with GPS Location , passing the current position only for an active observer. Notice that the class updates its value on the onLocationChanged method, passing to the current active observer the refreshed Location data.
4. The MutableLiveData Helper Class
The MutableLiveData is a helper class that extends LiveData , and exposes postValue and setValue methods. Other than that, it behaves exactly like its parent. To use it, define the type of data that it holds, like MutableLiveData to hold a String , and create a new instance.
To send updates to an observer, call postValue or setValue . The behavior of these methods is quite similar; however, setValue will directly set a new value and can only be called from the main thread, while postValue creates a new task on the main thread to set the new value and can be called from a background thread.
It is important to consider that, since the postValue method creates a new Task and posts on the main thread, it will be slower than direct calls to setValue .
5. Transformations of LiveData
Eventually, you’ll need to change a LiveData and propagate its new value to its observer. Or maybe you need to create a chain reaction between two LiveData objects, making one react to changes on another. To deal with both situations, you may use the Transformations class.
Map Transformations
Transformations.map applies a function on a LiveData instance and dispatches the result to its observers, giving you the opportunity to manipulate the data value.
It is very easy to implement Transformations.map . All you’ll have to do is provide a LiveData to be observed and a Function to be called when the observed LiveData changes, remembering that the Function must return the new value of the transformed LiveData .
Suppose that you have a LiveData that must call an API when a String value, like a search field, changes.
SwitchMap Transformations
The Transformations.switchMap is quite similar to Transformations.map , but it must return a LiveData object as a result. It’s a little harder to use, but it allows you to construct powerful chain reactions.
In our project, we used Transformations.switchMap to create a reaction between LocationLiveData and ApiResponse .
- Our Transformation.switchMap observes LocationLiveData changes.
- The LocationLiveData updated value is used to call the MainRepository to get the weather for the specified location.
- The repository calls the OpenWeatherService that produces a LiveData> as a result.
- Then, the returned LiveData is observed by a MediatorLiveData , which is responsible for modifying the received value and updating the weather exhibited in the view layer.
Watch out for time-consuming operations in your LiveData transformations, though. In the code above, both Transformations methods run on the main thread.
6. The MediatorLiveData
MediatorLiveData is a more advanced type of LiveData . It has capabilities very similar to those of the Transformations class: it is able to react to other LiveData objects, calling a Function when the observed data changes. However, it has many advantages when compared with Transformations , since it doesn’t need to run on the main thread and can observe multiple LiveData s at once.
To observe a LiveData , call addSource(LiveData , Observer ) , making the observer react to the onChanged method from the given LiveData . To stop the observation, call removeSource(LiveData ) .
In our project, the data observed by the view layer that contains the weather to exhibit is a MediatorLiveData . The component observes two other LiveData objects: weatherByLocationResponse , which receives weather updates by location, and weatherByCityResponse , which receives weather updates by city name. Each time that these objects are updated, weatherByCityResponse will update the view layer with the current weather requested.
In the MainViewModel , we observe the LiveData and provide the weather object to view.
In the MainActivity , the weather is observed and its result is shown to the user.
The MediatorLiveData was also used as the basis of an object that handles calls to the OpenWeatherMap API. Take a look at this implementation; it’s more advanced than the ones above, and it’s really worth studying. If you’re interested, take a look at OpenWeatherService , paying special attention to the Mediator class.
7. Conclusion
We’re almost at the end of our exploration of Android’s Architecture Components. By now, you should understand enough to create some powerful apps. In the next post, we’ll explore Room , an ORM that wraps SQLite and can produce LiveData results. The Room component fits perfectly in this Architecture, and it is the final piece of the puzzle.
See you soon! And in the meantime, check out some of our other posts on Android app development!
Источник
Урок 2. LiveData
В этом уроке рассмотрим основные возможности LiveData. Как подписаться на его данные. Как помещать в него данные. Как он взаимодействует со своими подписчиками.
Полный список уроков курса:
Подключение библиотеки
В build.gradle файл проекта добавьте репозитарий google()
В build.gradle файле модуля добавьте dependencies:
Если у вас студия ниже 3.0 и старые версии Gradle и Android Plugin, то подключение будет выглядеть так:
Иногда может возникнуть конфликт с support library. Будет выдавать такую ошибку: Error:Program type already present: android.arch.lifecycle.LiveData
В таком случае попробуйте указать последние версии двух этих библиотек.
Теория
LiveData — хранилище данных, работающее по принципу паттерна Observer (наблюдатель). Это хранилище умеет делать две вещи:
1) В него можно поместить какой-либо объект
2) На него можно подписаться и получать объекты, которые в него помещают.
Т.е. с одной стороны кто-то помещает объект в хранилище, а с другой стороны кто-то подписывается и получает этот объект.
В качестве аналогии можно привести, например, каналы в Telegram. Автор пишет пост и отправляет его в канал, а все подписчики получают этот пост.
Если вы знакомы с RxJava, то LiveData напомнит вам BehaviourSubject. Методом onNext вы передаете ему данные, а он передает эти данные своим подписчикам. Плюс, все новые подписчики сразу получают последнее значение.
Казалось бы, ничего особо в таком хранилище нет, но есть один очень важный нюанс. LiveData умеет определять активен подписчик или нет, и отправлять данные будет только активным подписчикам. Предполагается, что подписчиками LiveData будут Activity и фрагменты. А их состояние активности будет определяться с помощью их Lifecycle объекта, который мы рассмотрели в прошлом уроке.
Получение данных из LiveData
Давайте рассмотрим пример.
Пусть у нас есть некий синглтон класс DataController из которого можно получить LiveData .
DataController периодически что-то там внутри себя делает и обновляет данные в LiveData. Как он это делает, мы посмотрим чуть позже. Сначала посмотрим, как Activity может подписаться на LiveData и получать данные, которые помещает в него DataController.
Код в Activity будет выглядеть так:
Получаем LiveData из DataController, и методом )» target=»_blank» rel=»noopener noreferrer»>observe подписываемся. В метод observe нам необходимо передать два параметра:
Первый — это LifecycleOwner. Напомню, что LifecycleOwner — это интерфейс с методом getLifecycle. Activity и фрагменты в Support Library, начиная с версии 26.1.0 реализуют этот интерфейс, поэтому мы передаем this.
LiveData получит из Activity его Lifecycle и по нему будет определять состояние Activity. Активным считается состояние STARTED или RESUMED. Т.е. если Activity видно на экране, то LiveData считает его активным и будет отправлять данные в его колбэк.
Если предыдущие два абзаца состоят из кучи незнакомых для вас слов, то посмотрите Урок 1. Lifecycle. Там мы подробно разобрали объект Lifecycle и его состояния.
Второй параметр — это непосредственно подписчик, т.е. колбэк, в который LiveData будет отправлять данные. В нем только один метод onChanged. В нашем примере туда будет приходить String.
Теперь, когда DataController поместит какой-либо String объект в LiveData, мы сразу получим этот объект в Activity, если Activity находится в состоянии STARTED или RESUMED.
Нюансы поведения
Распишу сразу несколько важных моментов в поведении LifeData.
Если Activity было не активно во время обновления данных в LiveData, то при возврате в активное состояние, его observer получит последнее актуальное значение данных.
В момент подписки, observer получит последнее актуальное значение из LiveData.
Если Activity будет закрыто, т.е. перейдет в статус DESTROYED, то LiveData автоматически отпишет от себя его observer.
Если Activity в состоянии DESTROYED попробует подписаться, то подписка не будет выполнена.
Если Activity уже подписывало свой observer, и попробует сделать это еще раз, то просто ничего не произойдет.
Вы всегда можете получить последнее значение LiveData с помощью его метода getValue.
Как видите, подписывать Activity на LiveData — это удобно. Поворот экрана и полное закрытие Activity — все это корректно и удобно обрабатывается автоматически без каких-либо усилий с нашей стороны.
Отправка данных в LiveData
Мы разобрались, как получать данные из LiveData, и каким образом при этом учитывается состояние Activity. Теперь давайте посмотрим с другой стороны — как передавать данные в LiveData.
В классе DataController переменная LiveData будет выглядеть так:
Наружу мы передаем LiveData, который позволит внешним объектам только получать данные. Но внутри DataController мы используем объект MutableLiveData, который позволяет помещать в него данные.
Чтобы поместить значение в MutableLiveData, используется метод setValue:
Этот метод обновит значение LiveData, и все его активные подписчики получат это обновление.
Метод setValue должен быть вызван из UI потока. Для обновления данных из других потоков используйте метод postValue. Он перенаправит вызов в UI поток. Соответственно, подписчики всегда будут получать значения в основном потоке.
Чуть более подробный пример с LiveData мы рассмотрим в Уроке 4, когда будем изучать ViewModel.
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник