Heard about ViewModel in android? Android ViewModel is an architecture component that is designed to store UI related data. According to Android Developer Website.
The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.
If you remember one of our last Retrofit Tutorial where we learned about fetching JSON data from a URL. Now if one of our activity has some data on it fetched from server, but if for some reasons (for example orientation changes) the activity is recreated it will fetch the data from the server again. Fetching the same data from the server is a waste of resources. An efficient solution here is to separate the view data ownership from UI Controller logic.
So, in this Android View Model tutorial we will do the same thing we did in the previous Retrofit Tutorial but here we will be using the ViewModel architecture.
Table of Contents
Setting Up Android Project with RecyclerView and Retrofit
Creating a new Android Studio Project
- As always the first step is creating a new Android Studio Project. And in that project we will see how to work with Android ViewModel.
- I have created a project using an EmptyActivity named AndroidViewModel.
API to fetch data
- You can use any URL if you have any, to fetch data. Here I am going to use the following URL.
https://www.simplifiedcoding.net/demos/marvel/
- The above URL is returning a list of Marvel Super Heroes. You can open the URL to see what it is returning. It is the same URL that we used while learning Retrofit JSON.
Adding Required Dependency
For this project we are going to use the following dependencies.
- RecyclerView – To display list of superheroes.
- Glide – To load image from URL.
- Retrofit – For fetching data from the URL.
- ViewModel – The architectural component
So the first we will add all the above mentioned dependencies in the app level build.gradle file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support.constraint:constraint-layout:1.1.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' //adding dependencies implementation 'com.android.support:recyclerview-v7:27.1.1' implementation 'com.squareup.retrofit2:retrofit:2.4.0' implementation 'com.squareup.retrofit2:converter-gson:2.4.0' implementation 'com.github.bumptech.glide:glide:4.3.1' annotationProcessor 'com.github.bumptech.glide:compiler:4.3.1' implementation "android.arch.lifecycle:extensions:1.1.0" implementation "android.arch.lifecycle:viewmodel:1.1.0" } |
After adding all the dependencies sync your project.
Creating the POJO class for Hero
- Create a new class named Hero.java and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
package com.example.belalkhan.androidviewmodel; public class Hero { private String name; private String realname; private String team; private String firstappearance; private String createdby; private String publisher; private String imageurl; private String bio; public Hero(String name, String realname, String team, String firstappearance, String createdby, String publisher, String imageurl, String bio) { this.name = name; this.realname = realname; this.team = team; this.firstappearance = firstappearance; this.createdby = createdby; this.publisher = publisher; this.imageurl = imageurl; this.bio = bio; } public String getName() { return name; } public String getRealname() { return realname; } public String getTeam() { return team; } public String getFirstappearance() { return firstappearance; } public String getCreatedby() { return createdby; } public String getPublisher() { return publisher; } public String getImageurl() { return imageurl; } public String getBio() { return bio; } } |
Setting Up RecyclerView
Creating RecyclerView and RecyclerView Layout
- Now come inside activity_main.xml and create a RecyclerView here, in this RecyclerView we will display the Hero List.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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=".MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerview" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout> |
- Now we will also create one more layout resource file for the RecyclerView.
- Create a layout resource file named recyclerview_layout.xml and write the following xml code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:id="@+id/imageView" android:layout_width="match_parent" android:layout_height="200dp" /> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:textAlignment="center" android:textAppearance="@style/Base.TextAppearance.AppCompat.Large" /> </LinearLayout> |
Creating RecyclerView Adapter
- Now we need the RecyclerView Adapter. We are going to use the below code for this. If you are not sure what it is all about, you can check the RecyclerView Tutorial.
- So just create a new class named HeroesAdapter.java and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
package com.example.belalkhan.androidviewmodel; import android.content.Context; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.bumptech.glide.Glide; import java.util.List; public class HeroesAdapter extends RecyclerView.Adapter<HeroesAdapter.HeroViewHolder> { Context mCtx; List<Hero> heroList; public HeroesAdapter(Context mCtx, List<Hero> heroList) { this.mCtx = mCtx; this.heroList = heroList; } @NonNull @Override public HeroViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(mCtx).inflate(R.layout.recyclerview_layout, parent, false); return new HeroViewHolder(view); } @Override public void onBindViewHolder(@NonNull HeroViewHolder holder, int position) { Hero hero = heroList.get(position); Glide.with(mCtx) .load(hero.getImageurl()) .into(holder.imageView); holder.textView.setText(hero.getName()); } @Override public int getItemCount() { return heroList.size(); } class HeroViewHolder extends RecyclerView.ViewHolder { ImageView imageView; TextView textView; public HeroViewHolder(View itemView) { super(itemView); imageView = itemView.findViewById(R.id.imageView); textView = itemView.findViewById(R.id.textView); } } } |
Setting Up RecyclerView
- Now come back to MainActivity.java and write the following codes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class MainActivity extends AppCompatActivity { RecyclerView recyclerView; HeroesAdapter adapter; List<Hero> heroList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyclerView = findViewById(R.id.recyclerview); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new LinearLayoutManager(this)); } } |
- Now we will setup the Retrofit to fetch data from URL.
Setting Up Retrofit
The API Interface
- Create a new interface Api.java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.example.belalkhan.androidviewmodel; import java.util.List; import retrofit2.Call; import retrofit2.http.GET; public interface Api { String BASE_URL = "https://simplifiedcoding.net/demos/"; @GET("marvel") Call<List<Hero>> getHeroes(); } |
- Till here we done the things that we have already learned in the previous tutorials. Now we will use ViewModel architecture to load the data in RecyclerView.
Using Android ViewModel to load data Asynchronously using Retrofit
Creating Android ViewModel
- For creating a ViewModel, we need to create a class that will extend the ViewModel class.
- Here I am creating a class named HeroViewModel.java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
package com.example.belalkhan.androidviewmodel; import android.arch.lifecycle.LiveData; import android.arch.lifecycle.MutableLiveData; import android.arch.lifecycle.ViewModel; import android.widget.Toast; import java.util.List; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class HeroesViewModel extends ViewModel { //this is the data that we will fetch asynchronously private MutableLiveData<List<Hero>> heroList; //we will call this method to get the data public LiveData<List<Hero>> getHeroes() { //if the list is null if (heroList == null) { heroList = new MutableLiveData<List<Hero>>(); //we will load it asynchronously from server in this method loadHeroes(); } //finally we will return the list return heroList; } //This method is using Retrofit to get the JSON data from URL private void loadHeroes() { Retrofit retrofit = new Retrofit.Builder() .baseUrl(Api.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); Api api = retrofit.create(Api.class); Call<List<Hero>> call = api.getHeroes(); call.enqueue(new Callback<List<Hero>>() { @Override public void onResponse(Call<List<Hero>> call, Response<List<Hero>> response) { //finally we are setting the list to our MutableLiveData heroList.setValue(response.body()); } @Override public void onFailure(Call<List<Hero>> call, Throwable t) { } }); } } |
- The fetch using Retrofit part is exactly the same here that we already learned in the Retrofit Tutorial.
- And the ViewModel thing is very easy, we only have a method named getHeroes(). Inside this method we are checking if the List is null then we will get it asynchronously. Remember we need to use MutableLiveData and LiveData classes for storing the List and the rest is managed by the ViewModel component. So you do not need to worry about other things.
Using the Android ViewModel for Displaying the Hero List
- Write the following code at the end of onCreate() method in MainActivity.java.
1 2 3 4 5 6 7 8 9 10 11 |
HeroesViewModel model = ViewModelProviders.of(this).get(HeroesViewModel.class); model.getHeroes().observe(this, new Observer<List<Hero>>() { @Override public void onChanged(@Nullable List<Hero> heroList) { adapter = new HeroesAdapter(MainActivity.this, heroList); recyclerView.setAdapter(adapter); } }); |
- Now you can run your application, to check if it is working or not.
As you can see it is working fine. The advantage of doing this way is, if our activity recreates then it will not fetch the data again making our application more efficient. And yes you can get my source code from the below given GitHub repository.
Android ViewModel Example Source Code
I hope you found this tutorial helpful. If you are having any confusion regarding this Android ViewModel tutorial then leave your comments. And please don’t forget to SHARE this post with your friends. Thank You.