Reading Time: 7 minutes

Among the wide range of tools that were released with Android Jetpack in Android Architecture Components, LiveData, and ViewModel are particularly useful. I’m creating this tutorial in response to Ashton Jones, who expressed there are very few examples for integrating these Architecture Components with Google Firestore.

In this tutorial you will learn how all these elements interact, creating a fully functional app with a backend (FireStore) and a frontend (Android app) based on Google technologies.

In this first part, we will start by building the Android project. For illustrating the use of LiveData and ViewModel, we will create an app simulating a shopping grocery list.

Download the starter project from here:

This is a simple Android Java Project, which was started with the instructions in this post. It just contains a MainActivity.java file and the corresponding layout. Once the Gradle sync finishes, run the app and you should see a HelloWorld screen like in Image 1 below.

Image 1. The initial view of the app

Setting Up the Project’s Architecture

Once you get this working you will create the folder structure of the app. Mobile architectures are widely discussed, but since Android introduced Architecture components they gave us a guideline pointing towards MVVM. In this first part of the project you will focus on the View and View Model.

Under the Android project view in the left panel, create two new packages. One called “View” and the other one called “ViewModel”. Then drag MainActivity.java inside the “View” package. The resulting project structure should be similar to the one shown below in Image 2.

Image 2. Resulting structure after adding the View and ViewModel packages.

The next step is to create the ViewModel. Right-click on the ViewModel package and select New > Java Class. Give it a name, preferably with the suffix ViewModel, I will name it MainActivityViewModel.java. With these two files, we are able to work with both LiveData and ViewModel.

Creating the ViewModel

Begin by creating your ViewModel. The first thing you need to do is to extend the Android Architecture Components ViewModel library. Your class definition should look like these:

public class MainActivityViewModel extends ViewModel {

and if it is not imported automatically, also add the required dependency with the following line of code:

import androidx.lifecycle.ViewModel;

Then you will create an object to hold the shopping list, this object needs to be a MutableLiveData List, of type String because your shopping list items are going to just be the names of the items.

In a more realistic scenario, you would probably have a ShoppingItem object, under your “model” package (which we didn’t create), and get the properties of this item, but I’m trying to create a minimal example. Create the list with this line of code inside the MainActivityViewModel class.

private MutableLiveData<List<String>> shoppingList;

The purpose of the above variable is to hold all the shopping list items in a LiveData object.

Getting LiveData asynchronously

In this first part of the project, you will be simulating the asynchronous call of the data with a timer. The first thing you need to do is to retrieve the list and then add it to the shoppingList variable created in the previous step. Add this function in the MainActivityViewModel.java class:

public LiveData<List<String>> getShoppingList() {
  if (shoppingList == null) {
    shoppingList = new MutableLiveData<>();
    loadShoppingList();
  }
  return shoppingList;
}

This code validates whether the shoppingList is empty and if that’s the case it creates a new object and loads the shopping list into it.

You will need another function to load the list, add it below getShoppingList() with the code below:

private void loadShoppingList() {
  // 1
  Handler myHandler = new Handler();
  // 2
  myHandler.postDelayed(() -> {
    // 3
    List<String> shoppingListSample = new ArrayList<>();
    shoppingListSample.add("Bread");
    shoppingListSample.add("Bananas");
    shoppingListSample.add("Peanut Butter");
    shoppingListSample.add("Eggs");
    shoppingListSample.add("Chicken breasts");

    // 4
    long seed = System.nanoTime();
    Collections.shuffle(shoppingListSample, new Random(seed));

    // 5
    shoppingList.setValue(shoppingListSample);
    
    // 6
  }, 5000);
}

Let’s review the code above separately because it’s a bit long:

  1. You create a Handler. The purpose of this handler is to hold the code in the function so that it executes after some time.
  2. You tell the handler to delay the execution by the number of seconds in its second parameter (line number 6).
  3. In these lines, you create a sample shopping list, that in the future will be populated by a third-party service: Firestore.
  4. The purpose of the shuffle is to see whether the data is refreshed at any point. One important aspect of LiveData is that unlike other types of data, it is not lost when certain actions take place, for example, when an orientation change happens.
  5. This line of code assigns the “downloaded” (simulated) data into the local object of MutableLiveData.
  6. As mentioned in point number 2, this is setting a delay of 5k milliseconds for the data to “load”.

Troubleshooting

If after the above steps you are getting errors in the project read this part to troubleshoot, otherwise, you can skip to the next part.

The most common problems you may have are:

a. The project should be set to Java 8

In the loadShoppingList() in point number two you may notice we have an arrow ‘->’, this is called a lambda expression and it is only supported in Java 8. To fix this go into File > Project Structure, and select Java 1.8 in the fields of Source Compatibility and Target Compatibility, like in the image below:

Image 3. Setting the project to Java 1.8

b. Importing the correct dependencies

It is common in Android and Java to have similar dependencies with different functionalities. When Android Studio imports dependencies automatically it sometimes gets the wrong one. Make sure your imports are like below:

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

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

If you still have trouble, take a look at the ViewModel resulting code below:

Image 4. Complete View Model Class

Testing the app

After adding the above code, the app should have no changes. It should compile and run successfully, but you will still be seeing the “Hello World” empty screen. This is because we only implemented the ViewModel, which is the communication between the Model, and the View, but we still don’t bind it with the View itself.

Image 5. App after adding the ViewModel

Connecting the View to the ViewModel

As mentioned above now you are going to work with the View, so that the data loaded from the ViewModel is displayed correctly.

Configuring the XML of the View

Open activity_main.xml, located in app > res > layout. Remove the “Hello World” text view, and add the code below:

<ProgressBar
    android:id="@+id/progressbar"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent" />

  <ListView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent" />

This code has two XML items.

  1. The Progress Bar is what is commonly known as a “spinner” which will be shown on the screen for as long as the info delays to load (5k milliseconds for what we already coded).
  2. The ListView, which will show the shopping list items once loaded.

If you run the app at this point you will see the Progress Bar (spinner) on the screen, but it will never show the list, as it is still not paired with the view code in MainActivity.java. Run it to see this and verify your code is working. Your app should look like in the screen below:

Image 6. Screen with the Progress Bar Spinning

Adding Android Lifecycle dependencies

For the View Model to work, you will need to add the lifecycle extensions dependency to build.gradle(Module: app)

implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'

Android Studio will request Gradle sync, do it and it should be successful.

Displaying the data

Now open MainActivity.java, which we moved in your View folder. In the onCreate method below the setContentView() line, add this code:

// 1
ListView listView = (ListView) findViewById(R.id.list);

// 2
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressbar);
progressBar.setVisibility(View.VISIBLE);

// 3
MainActivityViewModel viewModel =
          ViewModelProviders.of(this).get(MainActivityViewModel.class);

// 4
viewModel.getShoppingList().observe(this, shoppingList -> {
   // 5
   ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
                                  android.R.layout.simple_list_item_1,
                                  android.R.id.text1, shoppingList);
  
  // 6
  listView.setAdapter(adapter);
  
  // 7
  progressBar.setVisibility(View.GONE);
});

Again long code, let’s review each line:

  1. You initialize the ListView and get it from the XML.
  2. The same for the progress bar, we initialize it and get the view from the XML. Then you set it to visible, to be shown while the data is loaded.
  3. Next, you initialize your ViewModel, using ViewModelProvider provided by Android Jetpack. In short, ViewModelProvider is a class that stores the view model and makes it available throughout the app so all the classes that need it can access the information.
  4. Once you have the ViewModel you call the getShoppingList() method, as you may recall that method is asynchronous, it will take 5 seconds and then return the data, that’s why you are observing the response.
  5. Once the shopping list is received, you create an array adapter to create the list view. You will be using layout.simple_list_item_1, which is a pre-provided XML for ListViews that are part of Android OS.
  6. Then you assign the adapter created in the previous step to the ListView.
  7. Finally, you hide the Progress View and show the data.

Required imports

Some imports are needed for this code to work, some will be done by Android Studio automatically and you will need to add others manually, here are all the imports that MainActivity.java should have:

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProviders;

import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;

import com.evanamargain.android.myshoppinglist.R;
import com.evanamargain.android.myshoppinglist.ViewModel.MainActivityViewModel;

With all this working you should be able to test the app and see your list appear after 5 seconds of waiting.

Image 7. Shopping List displayed in the screen

Great! We now have an app implementing LiveData and ViewModel, in the next part, we will integrate Firestore to this app so the data is loaded from a remote source. If you want to see how the project should work at the end you can download the finished project here:

If you liked the content on this post, please subscribe at the bottom of the page. If you have any doubts don’t hesitate to leave a comment and I will reply as soon as possible. You can also donate a coffee to support me so I can keep on creating things like this!

Until next time!

Evana Margain Puig

(Visited 113 times, 8 visits today)