Report this

What is the reason for this report?

Android MVVM LiveData Data Binding

Published on August 3, 2022
Anupam Chugh

By Anupam Chugh

Android MVVM LiveData Data Binding

We’ve already implemented MVVM using Data Binding and covered LiveData and Data Binding in separate tutorials. Today, we’ll use LiveData with Data Binding in our MVVM Android Application. We’ll see how LiveData makes it easy to update the UI from the ViewModel.

MVVM LiveData Data Binding

Up until now, we’ve used Data Binding to update the View from the ViewModel. LiveData is a handy data holder that acts as a container over the data to be passed. The best thing about LiveData is that it is lifecycle aware. So if you are in the background, the UI won’t try to update. This saves us from a lot of crashes at runtime. We’ll use the MutableLiveData class since it provides public methods setValue() and getValue(). Let’s create a simple Login Application using the above concepts. We will first use LiveData as well as Two-way Data Binding and then refactor the Data Binding Observables to LiveData completely.

Getting Started

Add the following dependency in your app’s build.gradle:


android {
    ...

    dataBinding {
        enabled = true
    }
    ...
}

dependencies {
    ...
    implementation 'android.arch.lifecycle:extensions:1.1.1'
    implementation 'com.android.support:design:28.0.0-beta01'
    ...
}

Project Structure

android-mvvm-livedata-databinding-project The LoginViewModelOld file would contain the old code and LoginViewModel file would contain the refactored code.

Model

We’ve defined our Model in the User.java class:

package com.journaldev.androidmvvmdatabindinglivedata;


import android.util.Patterns;

public class User {

    private String mEmail;
    private String mPassword;


    public User(String email, String password) {
        mEmail = email;
        mPassword = password;
    }

    public String getEmail() {
        if (mEmail == null) {
            return "";
        }
        return mEmail;
    }


    public String getPassword() {

        if (mPassword == null) {
            return "";
        }
        return mPassword;
    }

    public boolean isEmailValid() {
        return Patterns.EMAIL_ADDRESS.matcher(getEmail()).matches();
    }


    public boolean isPasswordLengthGreaterThan5() {
        return getPassword().length() > 5;
    }

}


Layout

The code for the activity_main.xml is given below:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="loginViewModel"
            type="com.journaldev.androidmvvmdatabindinglivedata.LoginViewModel" />
    </data>


    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="8dp"
            android:orientation="vertical">

            <android.support.design.widget.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:error="@{loginViewModel.errorEmail}"
                app:errorEnabled="true">

                <EditText
                    android:id="@+id/inEmail"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="Email"
                    android:inputType="textEmailAddress"
                    android:padding="8dp"
                    android:text="@={loginViewModel.email}" />

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

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

                <EditText
                    android:id="@+id/inPassword"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="Password"
                    android:inputType="textPassword"
                    android:padding="8dp"
                    android:text="@={loginViewModel.password}" />

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


            <Button
                android:id="@+id/button"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:onClick="@{()-> loginViewModel.onLoginClicked()}"
                android:text="LOGIN" />


            <ProgressBar
                android:id="@+id/progressBar"
                style="?android:attr/progressBarStyleLarge"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:layout_marginTop="8dp"
                android:visibility="@{loginViewModel.busy}" />


        </LinearLayout>

    </ScrollView>

</layout>

The ProgressBar would be displayed to simulate the login feature.

ViewModel

The code for the LoginViewModel.java is given below:

package com.journaldev.androidmvvmdatabindinglivedata;

import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.databinding.ObservableField;
import android.os.Handler;
import android.support.annotation.NonNull;


public class LoginViewModel extends BaseObservable {
    private String email;
    private String password;
    private int busy = 8;
    public final ObservableField<String> errorPassword = new ObservableField<>();
    public final ObservableField<String> errorEmail = new ObservableField<>();

    public LoginViewModel() {
    }

    private MutableLiveData<User> userMutableLiveData;

    LiveData<User> getUser() {
        if (userMutableLiveData == null) {
            userMutableLiveData = new MutableLiveData<>();
        }

        return userMutableLiveData;
    }

    @Bindable
    @NonNull
    public String getEmail() {
        return this.email;
    }

    public void setEmail(@NonNull String email) {
        this.email = email;
        notifyPropertyChanged(BR.email);
    }

    @Bindable
    @NonNull
    public String getPassword() {
        return this.password;
    }

    public void setPassword(@NonNull String password) {
        this.password = password;
        notifyPropertyChanged(BR.password);
    }

    @Bindable
    public int getBusy() {
        return this.busy;
    }

    public void setBusy(int busy) {
        this.busy = busy;
        notifyPropertyChanged(BR.busy);
    }


    public void onLoginClicked() {

        setBusy(0); //View.VISIBLE
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {


                User user = new User(getEmail(), getPassword());

                if (!user.isEmailValid()) {
                    errorEmail.set("Enter a valid email address");
                } else {
                    errorEmail.set(null);
                }

                if (!user.isPasswordLengthGreaterThan5())
                    errorPassword.set("Password Length should be greater than 5");
                else {
                    errorPassword.set(null);
                }

                userMutableLiveData.setValue(user);
                setBusy(8); //8 == View.GONE

            }
        }, 5000);
    }
}



An ObservableField is an object wrapper to make it observable. In the above code, we’ve encapsulated User inside a LiveData. Every time the user object is changed it will be observed in the MainActivity and the appropriate action would be taken. When the button is clicked, we set the ProgressBar to Visible. View.VISIBLE = 0. View.GONE == 8 After a delay of 5 seconds, the email and password are validated and the TextInputLayout bindables are updated.

ObservableField isn’t lifecycle aware.

The MainActivity.java class is given below:

package com.journaldev.androidmvvmdatabindinglivedata;

import android.arch.lifecycle.Observer;
import android.databinding.DataBindingUtil;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;

import com.journaldev.androidmvvmdatabindinglivedata.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        LoginViewModel loginViewModel = new LoginViewModel();
        binding.setLoginViewModel(loginViewModel);

        loginViewModel.getUser().observe(this, new Observer() {
            @Override
            public void onChanged(@Nullable User user) {
                if (user.getEmail().length() > 0 || user.getPassword().length() > 0)
                    Toast.makeText(getApplicationContext(), "email : " + user.getEmail() + " password " + user.getPassword(), Toast.LENGTH_SHORT).show();
            }
        });

    }
}

In the above code, the observe method looks for changes in the User object that was contained in the MutableLiveData. It displays a toast with the username and password. Now, let’s replace the ObservableField with LiveData completely.

Refactoring ObservableField to LiveData

The code for the new LoginViewModel.java class is given below:

package com.journaldev.androidmvvmdatabindinglivedata;

import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.ViewModel;
import android.os.Handler;


public class LoginViewModel extends ViewModel {


    public MutableLiveData<String> errorPassword = new MutableLiveData<>();
    public MutableLiveData<String> errorEmail = new MutableLiveData<>();

    public MutableLiveData<String> email = new MutableLiveData<>();
    public MutableLiveData<String> password = new MutableLiveData<>();
    public MutableLiveData<Integer> busy;

    public MutableLiveData<Integer> getBusy() {

        if (busy == null) {
            busy = new MutableLiveData<>();
            busy.setValue(8);
        }

        return busy;
    }


    public LoginViewModel() {

    }

    private MutableLiveData<User> userMutableLiveData;

    LiveData<User> getUser() {
        if (userMutableLiveData == null) {
            userMutableLiveData = new MutableLiveData<>();
        }

        return userMutableLiveData;
    }


    public void onLoginClicked() {

        getBusy().setValue(0); //View.VISIBLE
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {


                User user = new User(email.getValue(), password.getValue());

                if (!user.isEmailValid()) {
                    errorEmail.setValue("Enter a valid email address");
                } else {
                    errorEmail.setValue(null);
                }

                if (!user.isPasswordLengthGreaterThan5())
                    errorPassword.setValue("Password Length should be greater than 5");
                else {
                    errorPassword.setValue(null);
                }

                userMutableLiveData.setValue(user);
                busy.setValue(8); //8 == View.GONE

            }
        }, 3000);
    }
}

The above class now extends ViewModel since we no longer need BaseObservable. Now, we’ve changed the ObservableFields to MutableLiveData. Changes in the MutableLiveData would be automatically updated in the layout thanks to Data Binding. Our MainActivity.java class is now updated to:

package com.journaldev.androidmvvmdatabindinglivedata;

import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.databinding.DataBindingUtil;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;

import com.journaldev.androidmvvmdatabindinglivedata.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        LoginViewModel loginViewModel = ViewModelProviders.of(this).get(LoginViewModel.class);
        binding.setLoginViewModel(loginViewModel);
        binding.setLifecycleOwner(this);
        

        loginViewModel.getUser().observe(this, new Observer() {
            @Override
            public void onChanged(@Nullable User user) {
                if (user.getEmail().length() > 0 || user.getPassword().length() > 0)
                    Toast.makeText(getApplicationContext(), "email : " + user.getEmail() + " password " + user.getPassword(), Toast.LENGTH_SHORT).show();
            }
        });

    }
}

ViewModelProviders.of can be used to create ViewModel instance too as done above. This method instantiates the ViewModel only once. Every subsequent call would reuse the instance. The LifecycleOwner is an interface that our Activity can bind to. The output of the above application in action is given below: android mvvm live data data binding demo As you can see, the Toast message isn’t shown when the user leaves the application. Since the LiveData is lifecycle aware. When you open the application again, the toast would be displayed. This brings an end to this tutorial. You can download the project from the link below:

AndroidMVVMDataBindingLiveData

Github Project Link

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the author

Anupam Chugh
Anupam Chugh
Author
Category:
Tags:
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Still looking for an answer?

Was this helpful?

Is ti possible to do with Background task (Async task)

- karthik

I just wanted to say thank you for writing a really useful article.

- dlm

I read regarding MVVM integration with LiveData your article is very useful for me, I hope this article is useful every MVVM beginner who wants to understand very initial level… Thank you so much

- Ram Bhawan

Will you have example MVVM databinding to display item in recycler from api and on item click display details of item

- sagar hudge

> busy.setValue(8); //8 == View.GONE You can safely use View class as your ViewModel class is any accessing Android framework classes. JVM based testing will not be run here. > public boolean isEmailValid() { > return Patterns.EMAIL_ADDRESS.matcher(getEmail()).matches(); > } Model class should not access Pattern class as it is based on android framework. MVVM was originally created to help unit test pieces.

- Rohit Sharma

Found data binding errors. ****/ data binding error ****msg:Cannot find the getter for attribute ‘android:text’ with value type java.lang.String on com.google.android.material.textfield.TextInputLayout. file:D:\Android\MyBoond\app\src\main\res\layout\activity_registration.xml loc:71:12 - 95:67 ****\ data binding error **** I am getting this error. If anyone can help me. I am newbie in databinding.

- Faheem

why use mutablelivedata binding two way instead of livedata?

- Nam Anh

Hi, the tutorial is fantastic and thanks a lot for this. However, I have a query though - In demonstrating the liveData example you had written this piece of code :- User user = new User(email.getValue(), password.getValue()); This code however violates the principle of Dependency Injection. What you be your recommendation to deal with this? Thanks in advance.

- Akash Biswas

clear explanation…thank you…continue your good work…

- kajendhiran

Please make tutorial on mvvm rx java . Waiting for your tutorial

- Muhammad Zawawi Bin Manja

Creative CommonsThis work is licensed under a Creative Commons Attribution-NonCommercial- ShareAlike 4.0 International License.
Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.