MVVM – Android’s Architecture Components Part 1 – ViewModel & LiveData

In the Google I/O 2017 event, Architecture Components were announced along with a recommended architecture guidelines. Over the years, many different Design Patterns/Architecture have been proposed and adopted among Android developers. With this new guideline, Google is finally voicing out their recommended architecture for Android apps.

In this blog post, we will be following the architectural diagram from Android official documentation to implement a sample MVVM android project:

Our Sample App

Google has opened source a sample project: https://github.com/googlesamples/android-architecture/tree/todo-mvvm-live/ as a blueprint of how they want the architecture and design pattern to look like when using LiveData and ViewModel. On our side, we reviewed this project and created a simple Login app based on the same architecture and MVVM design pattern. Our Login app will require two inputs: email & password, which will be validated locally, make an API call to send the request. When API service returns a success/failure response, we will need to update the UI accordingly. The app will support for both portrait and landscape and include client side validation. All of this will be done in a very elegant way using Data Binding, LiveData, ObservableFields and ViewModel.

The app is Open Source and is available on Github: https://github.com/devansh-ramen/Android-App-Architecture-MVVM

View (Activities/Fragments), ViewModel and LiveData

For this blog post, we will focus on top two components “Activity/Fragment” and “ViewModel/LiveData”. So the “View-ViewModel” of the MVVM. Two new components were introduced from Android Team this year: LiveData and ViewModel which aims to make our life easier when building reactive UI:

LiveData

LiveData is an observable and lifecycle-aware data holder. It is similar to Rx-Observable data holders and similarly we will have an “observer” who will observe and trigger some action when the value of the observable object changes. Additionally, LiveData is also lifecycle aware. This will be particularly useful when we want to support for ConfigurationChanges (e.g phone rotates). If you have used RxJava, you will recall that we need to subscribe() and unsubscribe(), e.g, onPause(), so that no observer publish a UI updates/changes when Fragment/Activity which is onPause state (e.g, moved to background) and cause a crash. LiveData will automatically not notify its observers once the “lifecycle owner” which could be an Activity or Fragment when they are moved in the background. When observing a LiveData, we will pass in lifecycle owner. The LiveData will normally be defined inside of our ViewModels and not in Fragments/Activities (Views).

MutableLiveData is a subclass of LiveData which allows you to update the value of the Object. We have two additional methods inside of MutableLiveData: setValue() and postValue() methods which allows you to update the value of LiveData which will notify any active observers. PostValue() is required when we are off the main thread (e.g, update the value from a background thread).

ViewModel

The ViewModel is a new component from Android Architecture-Components. Activities/Fragments are recreated when the phone is rotated and all data are lost on Configuration changes (e.g, phone rotation). On the other hand, ViewModel is lifecycle-aware and data stored inside of ViewModel are not lost on Configuration Changes. As a result, with this pattern, all UI-data will be inside of the ViewModels so as to support Configurations Changes without having to much boilerplates. Another positive note is that Activities/Fragments are View and shouldn’t be concern/dealing with Data (check Single Responsibility Principle).

Two-way Data Binding

Data binding is a key component for implementing the MVVM pattern. Since we want data to be stored inside of the ViewModel, we will have ObservableFields which will be update when views are updated. Data binding can be used to directly bind Views from the layout files so that any updates on the views are pushed to the fields inside of the ViewModel.

In our layout for our example, we will have two fields “edt_email” and “edt_password”. We want to have the fields inside of the ViewModels. So text displayed will be managed from the ViewModels:

public final ObservableField<String> email = new ObservableField<>();
public final ObservableField<String> password = new ObservableField<>();

Additionally, we will have two fields for displaying the error messages for Email and Password fields respectively:

public final ObservableField<String> errorEmail = new ObservableField<>();
public final ObservableField<String> errorPassword = new ObservableField<>();

(For more details about the Observable fields – https://developer.android.com/reference/android/databinding/ObservableField.html)

Layout Binding:

<data>
    <variable name="viewModel" type="com.devanshramen.loginapplication.ui.login.LoginViewModel"/>
</data>
.
.
.
<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:error="@{viewModel.errorEmail}">

    <EditText
       android:id="@+id/edt_email"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:hint="@string/prompt_username"
       android:inputType="textEmailAddress"
       android:text="@={viewModel.email}"
       android:maxLines="1"/>

</android.support.design.widget.TextInputLayout>

<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:error="@{viewModel.errorPassword}">

    <EditText
       android:id="@+id/edt_password"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:hint="@string/prompt_password"
       android:imeActionId="@+id/login"
       android:imeOptions="actionUnspecified"
       android:inputType="textPassword"
       android:text="@={viewModel.password}"
       android:maxLines="1"/>

</android.support.design.widget.TextInputLayout>

android:text=”@={viewModel.email}”: The ObservableFields “ObservableField<String> email” from inside of ViewModel will be updated as the value of the EditText text property is updated.

Validate input & displaying error messages from the ViewModel

Since we bind error text for TextInputLayout: “@{viewModel.errorPassword}” inside of our layout file, we can easier display the error when required from within our ViewModel class. We have a validation method inside of the LoginViewModel which is trigger when we click on the Login Button:

public boolean validateInputs() {
    boolean isValid = true;

    if (email.get() == null || !EmailUtils.isEmailValid(email.get())) {
        errorEmail.set("Invalid Email");
        isValid = false;

    } else {
        errorEmail.set(null);
    }

    if (password.get() == null || password.get().length() < 4) {
        errorPassword.set("Password too short");
        isValid = false;
    } else {
        errorPassword.set(null);
    }

    return isValid;
}

Data binding allows us to display the error message by only dealing the ObservableFields and from inside of the ViewModel without getting access to the actual Views.

Configuration Changes (Phone rotation):

Since we have the data inside of the ViewModel, all the fields values are kept after configurations changes:

Portrait:

Screenshot_1511118656.png

Landscape:

Screenshot_1511118650.png

Observing LoginResponse using LiveData:

In our example, we want to be able to observe when the “LoginResponse” from our Login API is made available. We will define LiveData (LoginResponse) inside of ViewModel and observe its value for changes inside of our LoginActivity so that we can update the UI if login is successful.

  1. Create an instance of LiveData to hold a certain type of data inside of ViewModel
public class LoginViewModel extends ViewModel {
    .
    .
    // Create a LiveData
    private MutableLiveData loginResponse;

    public MutableLiveData getLoginResponse() {
        if (loginResponse == null) {
            loginResponse = new MutableLiveData();
        }
        return loginResponse;
    }
    .
    .
}

2. Create an Observer inside of Fragment or Activity (View) to wait for LoginResponse from the LoginActivity:

// The observer updates the UI when Login Response is successful
mViewModel.getLoginResponse().observe(this, loginResponse -> {

    if (loginResponse.isSuccess()) {
        Toast.makeText(LoginActivity.this, "Login success", Toast.LENGTH_SHORT).show();

        Intent intent = new Intent(this, MainActivity.class);
        startActivity(intent);
        finish();

    } else {
        Toast.makeText(LoginActivity.this, "Login failed", Toast.LENGTH_SHORT).show();
    }
});

When the value stored in the LiveData object is updated, the observer will be triggered and we can take the necessary actions, e.g, here we will open the MainActivity.

API call & Part 2

public void onBtnLoginClick() {

    if (validateInputs()) {

        // TO DO Get from UserRepository
        getAPIService().login(new LoginRequest(email.get(), password.get()))
                .compose(RxUtils.applySchedulers())
                .subscribe(
                        (LoginResponse response) -> {  // on Success
                            loginResponse.setValue(response);

                        },
                        (Throwable e) -> { // on Fail
                            e.printStackTrace();

                        },
                        () -> { // on Complete
                        });
    }
}

Note that the API call is currently inside of the ViewModel class, in the next part, we will need to update this in part 2. The API service call will be inside of the UserRespository class. Other than this we we have completed the “View-ModelView” part of the MVVM pattern using: Data binding, LiveData and ViewModel.

Conclusion

There are a lot of advantages of using MVVM pattern with the new Architectural Components release by the Android Team themself but it might take a bit more time to get started (e.g MVP pattern: If you want to check our previous blog post on the MVP pattern: https://devanshramen.com/2017/08/14/intro-to-mvp-pattern-for-android-di-using-dagger-and-unit-testing/). However it offers many advantages and does require any android developer to experience on. At the end of the day, there are always pros and critics between using different Design Patterns and you need to understand choose what will make you more productive as a developer. In the next part, we will discuss our the “Model” part of MVVM which will include Room DAO for local data and Retrofit for remote data.

If this post was useful to you, hit the like and share button. If you have any questions, please leave a comment below. Additionally, you can also hit a star button on github repo: https://github.com/devansh-ramen/Android-App-Architecture-MVVM

Cheers,

Please Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s