Skip to main content

How to use LiveData and ViewModel as Async Task Loader

We often required to load some data which take a long time to load. For this, we use the Async Task Loader to load the data in the background. But now we have an alternative for this work and we can use the LiveData and ViewModel as Async Task Loader. In this article, I am going to explain how to use the LiveData and ViewModel as Async Task Loader.

LiveData and ViewModel as Async Task Loader


Here to explain the use of LiveData and ViewModel to load the data like Async Task Loader we will create an app to list all installed package on a device. Below is the sample of the app which we are going to develop in this article of How to use LiveData and ViewModel as Async Task Loader.

List all installed app from android device

In this Article, I am going to create a demo app to list all app installed on your device. We will create four class and will go with each of the class one by one.
AppData.java: It is the POJO to store the details of the apps like AppName, Package Name, Icon.
AppDataModel.java: It is the main class in which we will implement the ViewModel and LiveData to retrieve and store the information of Apps.
MainActivity.java: MainActivity is our main class or only one activity of this demo App to list all apps from android device.
AppsAdapter.java: AppsAdapter class is our adapter of recyclerview.

Create POJO Class to Store the detail of the all installed app


AppData.java is the POJO class which contains four field appName, packageName, Icon, and mFile.  Apart from this, we have getter and setter of the variable. Below is the full code of AppData class.
AppData.java

package com.nplix.applist;

import android.graphics.drawable.Drawable;

import java.io.File;

public class AppData {
private String appName;
private String packageName;
private Drawable Icon;

public File getFile() {
return mFile;
}

public void setFile(File mFile) {
this.mFile = mFile;
}

private File mFile;

public String getAppName() {
return appName;
}

public void setAppName(String appName) {
this.appName = appName;
}

public String getPackageName() {
return packageName;
}

public void setPackageName(String packageName) {
this.packageName = packageName;
}

public Drawable getIcon() {
return Icon;
}

public void setIcon(Drawable icon) {
Icon = icon;
}
}

There is nothing special in above POJO class it is a normal POJO that we use in our day to day task as a developer. The real magic is in our next class that is AppDataModel, so let's create the AppDataModel class.

Create AppDataModel class with ViewModel and LiveData


Let me explain more about our ViewModel and LiveData class.  We have to create our AppDataModel class by extending the ViewModel.In the normal scenario, we can't store context or activity in ViewModel, but as we are going to extract the information about the installed app so we must require the context if we want to retrieve the information from our AppDataModel class. If you want to learn about the LiveData and ViewModel you can check out the article on LiveData and ViewModel which we published recently.

As we described above we can't use the context inside the ViewModel. So for the solution of this the AndroidViewModel so we will extend the AndroidViewModel instead of ViewModel as given below.
public class AppDataModel extends AndroidViewModel {
private AppLiveData appLiveData;
public AppLiveData getAppLiveData() {
return appLiveData;
}
public AppDataModel(@NonNull Application application) {
super(application);
}
}

Difference between ViewModel and AndroidViewModel


The major difference between both classes is context. In normal ViewModel, we can't use the context and activity due to the chance of memory leakage issue, but in AndroidViewModel we can use the context. For using the context its mandatory to create a default constructor with Application as the argument.
public AppDataModel(@NonNull Application application) { super(application); }

Create LiveData class to retrieve the Apps details


LiveData is the class which can be observed and notifies the bound activity and fragment in case data has been changed. Even LiveData observer can update the UI of the app on data change. If you want are not familiar with it, then check my recent article on LiveData.

Below is the full code of our LiveData class.
public class AppLiveData extends MutableLiveData<List<AppData>>{
private PackageManager packageManager;
List<AppData> appList;

private final Context context;

public AppLiveData(Context context){
this.context=context;
packageManager=context.getPackageManager();
LoadAppInfo();
}

private void LoadAppInfo() {
// Retrieve all known applications.
List<ApplicationInfo> apps = packageManager.getInstalledApplications(
PackageManager.GET_UNINSTALLED_PACKAGES);
if (apps == null) {
apps = new ArrayList<ApplicationInfo>();
}
appList = new ArrayList<AppData>(apps.size());
//Loop to get the info for all installed app and store the information in live data
for (int i=0; i<apps.size(); i++) {
AppData entry = new AppData();
entry.setPackageName(apps.get(i).packageName);
entry.setAppName(apps.get(i).loadLabel(context.getPackageManager()).toString());
entry.setFile(new File(apps.get(i).sourceDir));
//Check if file exist then get the icon and store into the POJO
if(entry.getFile().exists()){
entry.setIcon(apps.get(i).loadIcon(context.getPackageManager()));
}
//Add the indviual app information into the List
appList.add(entry);
Log.d("LiveData",entry.getAppName());
}
//Set the value into LiveData
setValue(appList);

}

}

Using below code we are declaring some variable that we required to retrieve and store the information of the installed app.

private PackageManager packageManager;
List<AppData> appList;
private final Context context;


In below code we are creating the constructor of our LiveData class and calling the LoadAppInfo() method to retrieve the app information.
public AppLiveData(Context context){
this.context=context;
packageManager=context.getPackageManager();
LoadAppInfo();
}

It is important to remind that from where context is LiveData constructor. So the answer to this is that it is coming from the ViewModel constructor call using the below code.

appLiveData=new AppLiveData(application);

You can check the code above for our LoadAppInfo() method it self-explanatory along with the comment. But still, if have any question then put in the comment section below.

Linking the ViewModel, LiveData, and Activity with help of observer


So far we have created the POJO and ViewModel for our demo app. Now we have to put the observer on LiveData so that whenever any change happens into stored data. Then observer notifies the Main Activity to update the UI.

So now we need to modify our main class. In Main class, first of all, we have to define our RecyclerView as given below. Here we are declaring the RecyclerView and assigning a simple LinearLayout.
RecyclerView recyclerView;
recyclerView=findViewById(R.id.recylerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));

Declare and assign adapter to recycler view. It is important to that we have not created adapter class yet will create later.
AppsAdapter appsAdapter;   
appsAdapter=new AppsAdapter(appsList,this,false,false);
recyclerView.setAdapter(appsAdapter);

Declare the AppDataModel class using the below code.
AppDataModel appDataModel;

Assign the value to declared appDataModel variable.
appDataModel= ViewModelProviders.of(this).get(AppDataModel.class);

Set observer on  LiveData using code below.
appDataModel.getAppLiveData().observe(this, new Observer<List<AppData>>() {
@Override
public void onChanged(@Nullable List<AppData> appData) {
//this is not required here but just for reference we can do any thing with this data.
appsList=appData;
//Update the data to adapter
appsAdapter.setData(appData);
//Update to the UI with latest data
appsAdapter.notifyDataSetChanged();
Log.d("MainActivity:", "Data has updated");
}
});

In above code, we are retrieving the data using getAppLiveData() method declared in our ViewModel class and set the observer on same.

Adapter for RecyclerView of LiveData and ViewModel


The complete source code of the Adapter class is as given below.
AppAdapter.java

package com.nplix.applist;

import android.content.Context;
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 java.util.List;


/**
* Created by Pawan on 1/18/2018.
*/
public class AppsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private static final int TYPE_FOOTER = 2;
private String TAG="LoadImage";

Context context;
private List<AppData> questionList;

private boolean mWithHeader;
private boolean mWithFooter;
private View.OnClickListener mOnClickListener;

public AppsAdapter(List<AppData> apps, Context context, boolean withHeader, boolean withFooter) {
this.questionList = apps;
this.context=context;
this.mWithHeader=withHeader;
this.mWithFooter=withFooter;

}
@Override
public int getItemViewType(int position) {

if (mWithHeader && isPositionHeader(position))
return TYPE_HEADER;
if (mWithFooter && isPositionFooter(position))
return TYPE_FOOTER;
return TYPE_ITEM;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {

if(viewType==TYPE_HEADER) {

return new header(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.header, viewGroup, false));
}
else if(viewType==TYPE_FOOTER){
return new footer(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.footer, viewGroup, false));
}
else {
View itemView = LayoutInflater.
from(viewGroup.getContext()).
inflate(R.layout.appitem, viewGroup, false);
VideoViewHolder holder = new VideoViewHolder(itemView);
itemView.setTag(holder);
itemView.setOnClickListener(mOnClickListener);

return holder;
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

if(holder instanceof header){

}
else if(holder instanceof footer){
((footer) holder).context = context;
}
else {
AppData appData=getItem(position);

((VideoViewHolder)holder).vName.setText(appData.getAppName());
((VideoViewHolder)holder).vPackageName.setText(appData.getPackageName());
((VideoViewHolder)holder).vImage.setImageDrawable(appData.getIcon());


}
}

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}


@Override
public int getItemCount() {
int itemCount=0;
if(questionList!=null) {


itemCount = questionList.size();
if (mWithHeader)
itemCount = itemCount + 1;
if (mWithFooter)
itemCount = itemCount + 1;
// return itemCount;
}
return itemCount;
}

private boolean isPositionHeader(int position) {
return position == 0;
}

private boolean isPositionFooter(int position) {
return position == getItemCount() - 1;
}
public void setOnClickListener(View.OnClickListener lis) {
mOnClickListener = lis;
}

protected AppData getItem(int position) {
return mWithHeader ? questionList.get(position - 1) : questionList.get(position);
}

private int getItemPosition(int position){
return mWithHeader ? position - 1 : position;
}

public void setData(List<AppData> questionList) {
this.questionList=questionList;
}

public class VideoViewHolder extends RecyclerView.ViewHolder {
protected ImageView vImage;
protected TextView vName,vPackageName;
protected Context context;

public VideoViewHolder(View v) {
super(v);
vImage = (ImageView) v.findViewById(R.id.image);
vName = (TextView) v.findViewById(R.id.name);
vPackageName=(TextView) v.findViewById(R.id.packageName);
}
public void clearAnimation() {
this.clearAnimation();
}
}

public class header extends RecyclerView.ViewHolder {
protected Context context;
protected int position;
public header(View v) {
super(v);
}
}

public class footer extends RecyclerView.ViewHolder {
protected Context context;
protected int position;
public footer(View v) {
super(v);
}
}
}

The above Adapter class is sample adapter which provides the following functionality, that can be further extended.
You can set onClick listener form activity by adapter.setOnClickListener method.

Set the data to adapter using setData method.

Create the Header and Footer by just giving the true and false value in adapter declaration.

We have done with this demo and now you are ready to test your app of "LiveData and ViewModel as Async Task Loader".

Complete Source code of LiveData and ViewModel as Async Task Loader


First of all, we are putting the XML file here.
appitem.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:orientation="horizontal"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:weightSum="100">

<ImageView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/image"
android:layout_weight="30"
android:layout_gravity="left"
android:fitsSystemWindows="true"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="70"
android:layout_gravity="right">
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"

android:textAppearance="@style/TextAppearance.AppCompat.Caption"
android:textIsSelectable="true"
android:textSize="20sp"
android:textStyle="bold"
android:visibility="visible" />

<TextView
android:id="@+id/packageName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Display1"
android:textIsSelectable="true"
android:textSize="16sp"
android:visibility="visible" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

 
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="com.nplix.applist.MainActivity">

<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/recylerView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

We have already given the complete code of the POJO and Adapter class above. Here we are giving the source code of the AppViewModel and MainActivity Class.
MainActivity.java

 
package com.nplix.applist;

import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
AppDataModel appDataModel;
RecyclerView recyclerView;
AppsAdapter appsAdapter;
List<AppData> appsList=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView=findViewById(R.id.recylerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));

appsAdapter=new AppsAdapter(appsList,this,false,false);
recyclerView.setAdapter(appsAdapter);

appDataModel= ViewModelProviders.of(this).get(AppDataModel.class);

appDataModel.getAppLiveData().observe(this, new Observer<List<AppData>>() {
@Override
public void onChanged(@Nullable List<AppData> appData) {
//this is not required here but just for reference we can do any thing with this data.
appsList=appData;
//Update the data to adapter
appsAdapter.setData(appData);
//Update to the UI with latest data
appsAdapter.notifyDataSetChanged();
Log.d("MainActivity:", "Data has updated");
}
});


}
}

 
AppDataModel.java

package com.nplix.applist;

import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.MutableLiveData;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.util.Log;

import java.io.File;
import java.util.ArrayList;
import java.util.List;


/**
* Created by PK on 1/15/2018.
*/

public class AppDataModel extends AndroidViewModel {
public AppLiveData getAppLiveData() {
return appLiveData;
}

private AppLiveData appLiveData;
public AppDataModel(@NonNull Application application) {
super(application);
appLiveData=new AppLiveData(application);
}



public class AppLiveData extends MutableLiveData<List<AppData>>{
private PackageManager packageManager;
List<AppData> appList;

private final Context context;

public AppLiveData(Context context){
this.context=context;
packageManager=context.getPackageManager();
LoadAppInfo();
}

private void LoadAppInfo() {
// Retrieve all known applications.
List<ApplicationInfo> apps = packageManager.getInstalledApplications(
PackageManager.GET_UNINSTALLED_PACKAGES);
if (apps == null) {
apps = new ArrayList<ApplicationInfo>();
}
appList = new ArrayList<AppData>(apps.size());

for (int i=0; i<apps.size(); i++) {
AppData entry = new AppData();
entry.setPackageName(apps.get(i).packageName);
entry.setAppName(apps.get(i).loadLabel(context.getPackageManager()).toString());
entry.setFile(new File(apps.get(i).sourceDir));
if(entry.getFile().exists()){
entry.setIcon(apps.get(i).loadIcon(context.getPackageManager()));
}
appList.add(entry);
Log.d("LiveData",entry.getAppName());
}
setValue(appList);

}

}
}

Conclusion


We have completed this article and you can see that it is very simple to use the LiveData and ViewModel as Async Task Loader. It's time to note some important point form this article. We have to use AndroidViewModel instead of ViewModel as it provides the context. LiveData can be observed so that we are storing our POJO data inside LiveData class. You can also download the complete source code of How to use LiveData and ViewModel as Async Task Loader from GitHub.

 

Comments

  1. […] How to use LiveData and ViewModel as Async Task Loader  […]

    ReplyDelete
  2. […] Components Library for MVC app development.  Architecture Components Library includes ViewModel, LiveData, RoomDatabase and Paging Library. In this article, we will create a TODO app that loads the paged […]

    ReplyDelete
  3. No Async load found in any code !?

    ReplyDelete

Post a Comment

Popular posts from this blog

Flutter How to Start Android Activity from Flutter View

Flutter and Dart is an excellent combination for creating the UI, but for accessing the platform-specific service we need to open platform-specific activity. So lets in this article we will explore how to start an android activity and access the service from Flutter View. Create a Project for this Android Activity Flutter View Demo Create a Project From File menu select the New Flutter Project Enter the project name Select the AndroidX support and click on next After the above, we step click on Finish We will have the following project structure created. Create the Second Activity in Android Just go to the android folder and open it in separate windows. We will have the following project structure. Create the Activity Just right-click on the Kotlin folder and create a blank activity from the menu. If you create the activity then you may be required to upgrade the Gradle and do some import. So Just click on update and wait for the project s

Kotlin Parcelable Array Objects Send To Activity

We know that IPC (Inter Process Communication) between the activity is an extremely important part of any application development. We often required that we need to send some data to other activity. For example, we may be required to send an array of data, data could be an Integer, String, Long, Double, Float or any other custom data objects. So, In this example, we are going to learn how to implement the Kotlin Parcelable Array object to send the data from one activity to second activity. What is Parcel? The parcel class is designed as a high-performance IPC transport. A Parcel can contain both flattened data that will be unflattened on the other side of the IPC, and references to live IBinde r objects that will result in the other side receiving a proxy IBinder connected with the original IBinder in the Parcel. Create Kotlin Parcelable Array Objects Parcelable is API for placing the arbitrary objects into the Parcel. In Actual in android app development, Parcelable is an interface

Scan and List All Available WiFi Network In Android

In this tutorial, we learn how to connect to a WiFi hotspot in android by code. In this Demo we will create an as small app which will scan the all available network or Hotspot and list down the network when you select specific network, the application will connect that particular network. You May Like Below Topic: How to Read and Write JSON data using GSON WP Android App using REST and volley WP Android App using REST and volley part2 Implementation of SwipeRefreshLayout in RecyclerView Create Amazing Bottom Navigation Bar Without Any External Library Introduction In this tutorial, we learn how to connect to a WiFi hotspot in android by code. In this Demo we will create an as small app which will scan the all available network or Hotspot and list down the network when you select specific network, the application will connect that particular network. We will add this functionality to our existing Demo app " Video Gallery ". If you would like to check out t