Android Architecture Components: LiveData

Paulina Szklarska
AndroidPub
Published in
4 min readDec 19, 2017

--

Hi! Welcome to the next article from the series about Android Architecture Components!

Lately, we discussed ViewModel useful for providing and maintaining data for your UI component as Activity or Fragment. If you didn’t read this, I encourage you to do it. Today we’ll talk about LiveData, which is a lifecycle-aware observable data holder. Let’s start!

Introduction

We’ll reuse example from the previous post about ViewModel, where we had some ViewModel class with the list of users inside:

public class UsersViewModel extends ViewModel {

private List<User> userList;

public List<User> getUserList() {
if (userList == null) {
usersList = loadUsers();
}
return userList;
}

private List<User> loadUsers() {
// do something to load users
}
}

Thanks to the ViewModel we don’t need to worry about many things, e.g. passing data to the recreated activity after configuration changes or memory leaks caused by ViewModel living longer than Activity.

But what if instead of getting the data every time we need it, we’ll be simply… notified every time there’s something new? Sounds familiar?

It’s possible thanks to LiveData, which is a lifecycle-aware data holder with the observer pattern! Let’s see what does it mean to you.

How does it work?

When we have a LiveData object (e.g. list of users), we can add some LifecycleOwner (it can be Activity or Fragment) as an observer to this data updates. Exactly as in the observer pattern, but with respecting lifecycle states. Thanks to this:

  • we (as an Activity or Fragment) we’ll be notified every time the data changes instead of requesting the data each time from ViewModel (so the UI is always updated!)
  • it’s all bounded with lifecycle, so we’ll be registered for observing those changes only if Activity (or any other LifecycleOwner) is in STARTED or RESUMED state and only then LiveData will emit any items (so no more memory leaks and NullPointerExceptions due to unexisting view!)

How to use it?

Firstly, we need to add proper dependencies into our app/build.gradle file:

implementation "android.arch.lifecycle:extensions:1.0.0"
annotationProcessor "android.arch.lifecycle:compiler:1.0.0"

Now let’s use a LiveData with the list of users from a previous example:

public class UsersViewModel extends ViewModel {    private MutableLiveData<List<User>> userLiveData = 
new MutableLiveData<>();
public UsersViewModel() {
setUserListRefreshCallback(newUserList -> {
userLiveData.postValue(newUserList);
});
}

public LiveData<List<User>> getUserList() {
return userLiveData;
}
}

Firstly, we wrap List<User> with MutableLiveData. We’d like to change LiveData only inside UsersViewModel class and that’s why there’s a mutable version of LiveData.

Every time there’s some change in the data (e.g. from some external source), we’d like to post a new value to LiveData. We can do it using methods as postValue() (asynchronously) or setValue() (synchronously, so we need to do it on the UI thread on our own).

Later, we can expose LiveData with a getter, so the Activity can use it easily:

public class UsersActivity extends AppCompatActivity {

@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

UsersViewModel usersViewModel =
ViewModelProviders.of(this).get(UsersViewModel.class);

usersViewModel.getUserList()
.observe(this, this::showUsers);
}
}

We see the difference in getting the data — now we’re adding ourselves as an observer to the user list changes. We pass to the observe() method two parameters — first one is LifecycleOwner (so basically our Activity) and thanks to this LiveData knows when it should notify Activity about changes. The second one is a callback with List<User> parameter, which we can use in showUsers() method.

From now on, we don’t need to worry about handling lifecycle states and registering/unregistering from this observable!

Doing more with LiveData

We can play a little with LiveData thanks to the Architecture Components classes. For example, if we’d like to make some changes on the LiveData before exposing it from ViewModel, we can do it by using Transformations:

Transformations.map()

If we don’t need the whole User objects, but only their names, we can use map() function:

LiveData<String> userNames = Transformations
.map(userLiveData, user -> {user.name});

MediatorLiveData

We can also merge multiple LiveData observables into one source by using MediatorLiveData. For example, if we’d like to merge data from the database and the network we can do it this way:

LiveData<List<User>> usersFromDatabase;
LiveData<List<User>> usersFromNetwork;

MediatorLiveData<List<User>> usersLiveData =
new MediatorLiveData<>();
usersLiveData.addSource(usersFromDatabase, newUserList ->
usersLiveData.setValue(value));
usersLiveData.addSource(usersFromNetwork, newUserList ->
usersLiveData.setValue(value));

Thanks to this Activity can observe only usersLiveData object and will be notified every time there’s a change in any of these.

LiveData & Room

If you use Room in your project (and if you don’t — why?) you should know that your queries can return LiveData objects:

@Dao
public interface UserDao {
@Query("SELECT * FROM users WHERE name=:name")
LiveData<List<User>> getUsersByName(String name);
}

Thanks to this, queries will be made asynchronously (so you don’t need to worry about making them on a proper thread) and you’ll have the observer pattern for free (so every time the content in the database will change, you’ll be notified about this!)

--

--

Paulina Szklarska
AndroidPub

Flutter GDE / Flutter & Android Developer / blogger / speaker / cat owner / travel enthusiast