Android Architecture Components: LiveData

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

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?

  • 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?

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

Transformations.map()

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

MediatorLiveData

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

@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!)

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

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store