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

WordPress Android App with REST API And Retrofit

Retrofit is the library which converts your JSON data into Java object. In this tutorial, we will learn about how to implement the Retrofit to create a WordPress Mobile App. As we all know, WordPress is the one of the biggest CMS (Content Management System) of the world. Wordpress full fill the requirements for all most every type of the website. So, in this tutorial, I am going to learn how to create an Android App using the WordPress REST API and Retrofit.
What is Retrofit?
If you want to know about Retrofit, I would like to give you just a simple one-line explanation of the Retrofit. It is a library which converts your REST HTTP request to Java interface. We will learn how we can do all these stuff using some very simple steps.

Further, you can check the Retrofit Library here.

So, In this tutorial, we will use the Retrofit to retrieve all post and there except ( A short Description of Post) form a WordPress website.
Prerequisite for using the Retrofit for WordPress Android App
There is s…

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 sync completed.
Update the project build config …

Flutter Theme Creation, Programmatic Way

Flutter and Dart is an excellent combination for creating the mobile app with material design with nice GUI. Its lots of widget which help the developer for creating the app UI very fast with respect to the other language. You may face some problem in starting due to comm, curly brace and semicolon but after some time you will find the way how to work with these all stuff and it becomes very easy for you. But in this article, we are not going to discuss all these. In this article, we will handle a very interesting topic on how to create a custom theme or multiple themes and let's user decide which theme to use.



Create a flutter project



So let's create a flutter project by selecting the appropriate option from the File menu in android studio or Visual Code. If you are new to Flutter and Dart you can check out our recent post to get started Creating Cross-platform app using flutter.



Once your project is created you will get the default counter app and you try running the app in the…