Basic Example of LiveData and ViewModel

Image From Android Pub

As i promised i am back with basic example of LiveData and ViewModel.Please look back to previous article also.

This app just shows list of fruits which i love most. When the application launch there will be progressbar which will be invisible after 5 seconds and list of fruits will be shown. And every time i have Shuffled the list of fruits. So what we are going to track is when data will be fully loaded and when viewmodel will provide it to us.

Cofigurations:

Build.gradle(app):

apply plugin: 'com.android.application'

android {
compileSdkVersion 25
buildToolsVersion '25.0.0'
defaultConfig {
applicationId "app.tutorial.livedatademo"
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
jackOptions {
enabled true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:25.3.1'

//for lifecycle and LiveData and ViewModel
compile 'android.arch.lifecycle:runtime:1.0.0-alpha1'
compile 'android.arch.lifecycle:extensions:1.0.0-alpha1'
annotationProcessor "android.arch.lifecycle:compiler:1.0.0-alpha1"
}

Currenrly the lifecycle libraries are not stable so they are not linked with stable AppcompactActivity or Fragment. So they are imported seperatly in android.arch.lifecycle group. Once the library will be stable this group will be deprecated and they will be available in Activity and fragment. So donot hesitate to use it. 😉😉

build.gradle (project)

allprojects {
 repositories {
 jcenter()
 maven { url 'https://maven.google.com' } 
 }
}

Add google maven url to this.

Now you are all setup to rockandroll. 🙌🙌🙌🙌

So lets have a look to our ViewModel.

ViewModel is the new class provided by lifecycle. It can be termed as the bridge between model and UI but quite intelligent one in the sense that it can be automatically retained in the case of orientation change .

Generally what happens during orientation change?

Suppose we are in one activity and the activity loads some fruits list and add it to the listview. Now when the orientation change is has not been handled then new instance of activity will be created and the loading of fruits takes place and is then again added to listview.

How does viewmodel solve this problem?

If you have used viewmodel along with LiveData class to store data then during orientation change new instance of activity will be created but the data wont be downloaded again. Viewmodel will provide the most recent available data. You will be able to see that in this example.

Note: Don’t think that viewmodel will hold data forever or for every case. If you close or activity is destoryed the viewmodel will also be destroyed or cleared. Because as i said in previous article that it has to address leakage also so its coupled with activity or fragment once if they are destroyed viewholder will also be cleared.
package app.tutorial.livedatademo;

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


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

/**
* Created by Taman Neupane on 5/21/17.
* for PeaceNepal Dot Com
* contact me if any issues on taman.neupane@gmail.com
*/

public class MainActivityViewModel extends ViewModel {

private String TAG = MainActivityViewModel.class.getSimpleName();

private MutableLiveData<List<String>> fruitList;

LiveData<List<String>> getFruitList() {
if (fruitList == null) {
fruitList = new MutableLiveData<>();
loadFruits();
}
return fruitList;
}

private void loadFruits() {
// do async operation to fetch users
Handler myHandler = new Handler();
myHandler.postDelayed(() -> {
List<String> fruitsStringList = new ArrayList<>();
fruitsStringList.add("Mango");
fruitsStringList.add("Apple");
fruitsStringList.add("Orange");
fruitsStringList.add("Banana");
fruitsStringList.add("Grapes");
long seed = System.nanoTime();
Collections.shuffle(fruitsStringList, new Random(seed));

fruitList.setValue(fruitsStringList);
}, 5000);

}

@Override
protected void onCleared() {
super.onCleared();
Log.d(TAG, "on cleared called");
}

}

Now lets look into the code. Let me first clear your

  1. What is MutableLiveData?

MutableLiveData is LiveData which is mutable. Simple ¿¿¿ 😂😂😂

Ok no more jokes. Its also LiveData as it extends LiveData internally and also the two magic methods of LiveData is publicly available in this and they are

  • setValue() : set the value and dispatch the value to all the active observers.
One thing to keep in mind about setvalue() is that it cannot be done in background thread it must be done in main thread only.
  • postValue() : post a task to main thread to override value set by setvalue.
As setvalue cannot be called from background thread so postvalue must be used to set value from background thread.
package app.tutorial.livedatademo;

import android.arch.lifecycle.LifecycleActivity;
import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;

public class MainActivity extends LifecycleActivity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.list);
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressbar);
progressBar.setVisibility(View.VISIBLE);
MainActivityViewModel model = ViewModelProviders.of(this).get(MainActivityViewModel.class);
model.getFruitList().observe(this, fruitlist -> {
// update UI
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1, android.R.id.text1, fruitlist);
// Assign adapter to ListView
listView.setAdapter(adapter);
progressBar.setVisibility(View.GONE);
});
}
}

On now the final step of connecting viewmodel with the UI. And it is also just tooooo simple or easy balls 😉😉.

As i said previously we have to use LifecycleActivity because LifecycleActivity is also the activity which has just implemented LifecycleOwner.

So why it is necessary? Why i am giving bla bla about lifecycle activity?

Because to get the instance of model we require ViewModelProviders.of and which in returns requires Lifecycleowner. So we have to extend our activity or fragment with lifecycle one but only for short period of time until the stable arrives.

Now once we have the viewmodel instance we can call that magic function.

model.getFruitList().observe(this, fruitlist-> {
 // update UI
 
 });

Ok now we call one of the method of viewmodel getfruitlist which returns livedata so we can use that magic function of livedata which is observe which in return takes our activity i.e. Lifecycle observer to find out active/inactive to dispatch second parameter in our case is fruitlist or any other data.

Any one not familiar with lambda expression don’t get confused. The function with arrow is just function representation with lambda expression its not special case of viewmodel or livedata.

Ok now its time for UI. But i have written any thing special there to explain. There is just a progressbar and listview inside framelayout. Progressbar shows loading of new data so we can find when actual loading takes place and when viewmodel provides without loading.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="app.tutorial.livedatademo.MainActivity"
>

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

<ProgressBar
android:id="@+id/progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>

<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>

</FrameLayout>

</LinearLayout>

First launch:

1.You can have a close look at the list both list are same which means my list didnot shuffled on orientation change because every time if list is loaded i have shuffled the list. You can also verify it by looking at progress bar. On orientation change progressbar is also not visible.

2. I have made the list loading time 5 sec because if your app goes in background in between that 5 sec then the viewmodel will also be destoryed and observer wont be called.

First launch

Second launch: similar for the second launch, i just wanted to show for different launch list is shuffled but for orientation change its not.

Second Launch

Now what are you looking at just open your laptop and try it out. Also dont forget to have a look at android developer. There are quite i interesting things available to dive into.

In this article i couldn’t explain interfragment communication and transformation which i will post in next article, i dont want to make it vague and others confused.

So please stay tuned if you liked it. Happy coding. And its already 2:30 AM. Goodnight 😴😴😴😴

UPDATE :

onSaveInstanceState() with ViewModel

Nishan Khadka Thankyou for your query about relation between onSaveInstanceState() and viewmodel. But first of all we need to know some basics about activity. We all know about this but let me describe it once as this is basic to understand for this topic.

Four condition of Activity closing :

  1. When activity is swipped closed. (Data stored in onSaveInstanceState() and ViewModel is destroyed)
  2. When activity is normally closed with back button (Data stored in onSaveInstanceState() and ViewModel is destroyed)
  3. When activity closed by using home button or any other factor (Activity is not destroyed , its only running in background,onSaveInstanceState() and ViewModel is not destroyed)
  4. When activity is rotated (Again activity is created ,onSaveInstanceState() and ViewModel is not destroyed )

Our concern is not for 1,2 because in those cases we need to reload data as usually. But for condition 3 and 4 , Lets have a look for condition 3, 4.

But first of all let’s have a look at some points regarding ViewModel and onSaveInstanceState().

ViewModel

  1. The data stored by ViewModel is not for long term. (Until activity is destroyed)
  2. They are not used to persist data.

onSaveInstanceState()

First of all we need to know when this function is called . This function is called when activity is not fully destroyed like:

  1. When activity goes to background (user pressed home button or any other factor)
  2. When activity configuration is changed.

Some demerits or limitation of onSaveInstanceState():

  1. We cannot store complex object in this function only small amount of data can be used.
Now from above description we know about when to use ViewModel and onSaveInstanceState().

But how to use ?

Lets have a look at two cases here , when activity opens from background from condition 3,4 as above.

  1. Your activity doesn’t require any extra data to load and all data is directly loaded from ViewModel you don’t need to do anything. ViewModel will handle everything for you.
  2. When ViewModel requires some extra data to load data to activity. In this case we need to store basic data to onSaveInstanceState() bundle and later we can pass store data to viewmodel and load data to our activity.
  3. But there might be one extra case if the data to be passed to viewmodel is complex. In this case we can add one extra thing to persist data i.e. database or shared preference.

Personally i don’t specify that you must follow this way but it totally depends upon you and condition how activity is started (Either if bundle is passed from another activity).

For more details . you can look into this google architecture

https://medium.com/androiddevelopers/viewmodels-persistence-onsaveinstancestate-restoring-ui-state-and-loaders-fc7cc4a6c090

Only you need to now when viewmodel and onSaveInstanceState() is destroyed and is called. Its totally upto you. :D :D

If you have any question and effective way to handle such scenario please share with me. I will be looking forward for response regarding this.

Happy Coding :D :D