How To Use Android ViewModel

This tutorial will explain the fundamentals of ViewModel and LiveData and how to use them by creating simple demo App that shows recent movies.


Baraa Abuzaid
@baraaabuzaid
How To Use Android ViewModel

In 2017 google released the Android architecture component, a set of libraries design to address a lot’s of challenges face developer when developing android apps. The libraries full of helpful components that shed the light into concepts that will potentially make android development fun again and at the center of all that was ModelView.

 

So What is ViewModel in the Android architecture component ?

ViewModel is a class that is designed to store and manage UI related data.
but what makes it shine is that it is lifecycle aware meaning, it will survive any configuration changes. remember how you used to store data on onSaveInstanceState() and load them back on onCreate(). And all of that was just to survive the moment when the user rotates his device. Now you could get rid of all that by using the ViewModel.
ViewModel object will remain in memory as long as the LifeCycle of the fragment or activity that’s scoped to it aren’t being removed permanently.
The diagram below shows the various lifecycle states that the activity might go through in comparison to ViewModel that’s scoped to it.

 

 

The second advantage that you’ll get by using ViewModel, is the ability to share data between fragments without worrying about whether the other fragment that receives the data is visible and ready to display the data or not.

 

ViewModel And LiveData

We can’t talk about ViewModel without mentioning the LiveData another awesome data class that available in Android architecture components.

So what is LiveData?
LiveData is an Observable data holder class. that is lifeCycle aware, and we usually use it in combination with ViewModel. its observe the data and whenever the data changes it automatically update the UI. but not only that it also smart enough to only update UI elements that is currently active and ignores it when it is been destroyed. Thanks to the LifecycleOwner interface that’s the LiveData Use to determine the state of the Fragment or the Activity.
So now your Fragment / Activity by default implemets LifecycleOwner interface.

Now Let’s put all of that in practice, by applying it into a simple app that shows movies currently available in cinemas.
For networking, we will use Retrofit combining with GSON for parsing JSON. so here are the dependencies we need.

ext {
    retrofitVersion = '2.0.2'
    lifecycle_version = "1.1.1"
}

dependencies {

    //rest interactions
    implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
    //JSON Parsing
    implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion"
    implementation 'com.google.code.gson:gson:2.8.0'
    //http logging
    implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
    
    // for using android Architecture components ViewModel
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"
}

 

In the ViewModel, we have three member variable isLoading, error and moviesList and notice they are all wrapped with MutableLiveData.
MutableLiveData allows us to read and write. on the other hand, we expose the variable outside our ViewModel as LiveData which is a read-only type. becuse we only going to display a list of movies in our view.

public class MovieViewModel extends ViewModel {
    private static final String TAG = "MovieViewModel";
    // to show a progressbar while loading a set of recent movies 
    MutableLiveData<Boolean> isLoading = new MutableLiveData<>();
    // display error if loading failed 
    MutableLiveData<Boolean> error = new MutableLiveData<>();
    // List of recent movies fetched online 
    MutableLiveData<List<MovieItem>> moviesList = new MutableLiveData<>();
    // use retrofit for making GET request to rest api 
    private MovieApiModule movieApi = new MovieApiModule();
    
    private Call<ResponseNowPlaying> apiCall;

    public MovieViewModel() {

        loadData(1);
    }

    public void loadData(int numPage){
        isLoading.setValue(true); 
        error.setValue(false);  
        apiCall = movieApi.providesMovieApiSevice().getNowPlaying(numPage);
        
        // Making GET request to rest api service using retrofit
        apiCall.enqueue(new Callback<ResponseNowPlaying>() {
            @Override
            public void onResponse(Call<ResponseNowPlaying> call, Response<ResponseNowPlaying> response) {
                // if there is no data set error and end 
                if(response.body() == null){
                    error.setValue(true);
                    isLoading.setValue(false);
                    return;
                }
                // store MovieList data to a list
                List<MovieItem> movieItemList = response.body().getResults();
                isLoading.setValue(false);
                moviesList.setValue(movieItemList);
            }

            @Override
            public void onFailure(Call<ResponseNowPlaying> call, Throwable t) {
                Log.e(TAG, "onFailure: ",t);
                isLoading.setValue(false);
                error.setValue(true);
            }
        });
    }

    public LiveData<Boolean> getIsLoading() {
        return isLoading;
    }

    public LiveData<Boolean> getError() {
        return error;
    }

    public LiveData<List<MovieItem>> getMoviesList() {
        return moviesList;
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        if (apiCall != null){
            apiCall.cancel();
            apiCall = null;
        }
    }
}

 

And then simply in the fragment onViewCreated  we start observing our LiveData.


public class MovieFragment extends Fragment{
@Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        viewModel = ViewModelProviders.of(this).get(MovieViewModel.class);

        obseverViewModel();
    }

    public void obseverViewModel(){
        viewModel.getError().observe(this,error->{
            if (error)
                Toast.makeText(getContext(),"Error! Loading",Toast.LENGTH_LONG).show();
        });

        viewModel.getIsLoading().observe(this,(isLoading)->{
            if (isLoading)
                progressBar.setVisibility(View.VISIBLE);
            else
                progressBar.setVisibility(View.GONE);
        });

        viewModel.getMoviesList().observe(this,movieItems -> {
            Log.e(TAG, ">> obseverViewModel: Is Observing .... List Size: "+movieItems.size());
            movies.addAll(movieItems);
            adapter.notifyDataSetChanged();
        });

    }
}

 

And wala..! now you know how to use the awesome ViewModel and LiveData. check out the full demo App on GitHub. don’t forget to replace API_KEY in the class MovieApiModule with your own API key. You could get your own developer API key for free from themoviedb.

Demo App on GitHub

 

 

Show Comments (0)

Comments

Related Articles

Software Design And Architecture

Why you should use MVP clean architecture in your next android project

Tech industry is moving at a staggering pace. Gone the good old days when a release cycle takes year or more. And we enter a new era. The era of Apps and “rapid release cycle”....

Posted on by Baraa Abuzaid