Android JetPack Manager Components Part1:(ViewModel+LiveData + Paging Library)

JetPack:

Jetpack is a set of components, tools and architectural guidance to help make it quick and easy to build great Android apps. It provides common infrastructure code so you can focus on what makes your app unique.

In Jetpack, Components are split into four categories which are Foundation, Architecture, Behaviour, UI.
ViewModel, LiveData, Room DB, Paging Library is the part of the Jetpack Architecture Components.

ViewModel:

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. ViewModel class allows data to survive configuration changes such as screen rotations.
for example, if the user rotates the screen, the UI has to recreate and also data holder will lose the data. Then we need to restore it once again. You can tell that we can use SavedInstanceState but when we come to big data, it won’t work out. But ViewModel has the best solution to that.
ViewModel class automatically retained the data when the configuration changes so we no need to worry about that.

Then Implement???

To add Jetpack components to your project, open the build.gradle file for your project (not the ones for your app or module) and add the google() repository as shown below:

allprojects {
repositories {
jcenter()
google()
}
}

To Implement ViewModel first we have to add the dependencies in build.gradle file for you app or module like below:

// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:1.1.1"
annotationProcessor "android.arch.lifecycle:compiler:1.1.1"

Implementation of ViewModel is very simple, let’s see the below code

public class MainViewModel extends AndroidViewModel
{

private Database appDatabase;
private final LiveData<List<User>> userList;

public MainViewModel(@NonNull Application application)
{
super(application);
appDatabase = Database.getDatabase(this.getApplication());
//Initialize User List
userList = appDatabase.userDao().getAllUsers();
}

//Add New User
public void addUser(User user)
{
new addAsyncTask(appDatabase).execute(user);
}

// Get Users
public LiveData<List<User>> getUserList()
{
return userList;
}

private static class addAsyncTask extends AsyncTask<User, Void, Void>
{

private Database db;

addAsyncTask(Database appDatabase)
{
db = appDatabase;
}

@Override
protected Void doInBackground(final User... params)
{
db.userDao().addUser(params[0]);
return null;
}

}
}

To implement the ViewModel we just extend the ViewModel class or AndroidViewModel class. Difference between AndroidViewModel and ViewModel is getting Application class instance in the constructor of ViewModel class. If you want the application class instance in the ViewModel constructor means you can extend the AndroidViewModel otherwise you can extend the ViewModel in the following method.

After the Above process, we can get the data from our UI components(Activity or Fragments) as shown in below:

MainViewModel mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);

We can get the our ViewModel using above way. Ikot tara

LiveData:

LiveData, which is a lifecycle-aware observable data holder. Unlike other observable, It respects Android lifecycles.

LiveData observes the update values always, When UI components(Activities and Fragments components is in the active state).
When the activity goes to the destroyed or stopped state, it won’t observe any data. so Memory leak will be avoided.

We will be notified every time when the data is changed so UI always updated

Execution how???

  1. Create an instance of LiveData to hold a certain type of data. ViewModel is the preferable way to do this.
private final LiveData<List<User>> userList;
userList = appDatabase.userDao().getAllUsers();

2)Create an Observer object that defines the onChanged() method, which controls what happens when the LiveData object’s held data changes. You usually create an Observer object in a UI Components( an Activity or Fragment).

3)Attach the Observer object to the LiveData object using the observe() method. The observe() method subscribes the Observer object to the LiveData object so that it is notified of changes. You usually attach the Observer object in a UI Components.

MainViewModel mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
mainViewModel.getUserList().observe(this, users -> {
if (MainActivity.this.userList == null) {
setListData(users);//update this users list in the UI
}
});

Benefits of LiveData:

  • Instead of updating the UI every time the app data changes, your observer can update the UI every time there’s a change
  • If the observer’s lifecycle is inactive, such as in the case of an activity in the back stack, then it doesn’t receive any LiveData events. So crashes will be avoided.
  • No need to handle the life cycle.
  • UI always getting updated when data changes because of the observer pattern.

for reference sample git repository https://github.com/arunpandian22/ViewModelLiveDataSample

Paging Library:

The Paging Library makes it easier for you to load data gradually and gracefully within your app’s RecyclerView.

The main benefit of the Paging library is less network bandwidth and quick respond while load the paging.

How to Use???

To use this Paging Library we have to add the dependency in app level or module level build.gradle file as shown in below:

implementation 'android.arch.paging:runtime:1.0.1'

1.PagedList:

Each instance of PagedList loads an up-to-date snapshot of your app’s data from its DataSource. Data flows from your app’s backend or database into the PagedList object

The PagedList class works with a PagedListAdapter to load items into a RecyclerView. These classes work together to fetch and display content as it’s loaded, prefetching out-of-view content and animating content changes

We can configure the PagedList like following:
PageSize — number of items per page
PrefetchDistance — defines how far from the edge of loaded content an access must be to trigger further loading

PagedList.Config config = new PagedList.Config.Builder().setPageSize(PAGE_SIZE).setPrefetchDistance(PREFETCH_DISTANCE).setPageSize(PAGE_SIZE).build();

2. LivePagedListBuilder

This class is useful to generate LiveData from the DataSource.Factory.

public LivePagedListBuilder livePlyerList=new LivePagedListBuilder(playerDao.liveListData(),new PagedList.Config.Builder().setPageSize(PAGE_SIZE).setPrefetchDistance(PREFETCH_DISTANCE).setPageSize(PAGE_SIZE).build());

3.DataSource:

It is a base class for loading data into the PagedList.

DataSource is queried to load pages of content into PagedList. A PagedList can grow as it loads more data, but the data loaded cannot be updated. If the underlying data set is modified, a new PagedList / DataSource pair must be created to represent the new data.

4.PagedListAdapter

RecyclerView.Adapter base class for presenting paged data from PagedLists in a RecyclerView

While using a LiveData<PagedList> is an easy way to provide data to the adapter, it isn’t required — you can use submitList(PagedList) when new lists are available.

MainViewModel mainViewModel =ViewModelProviders.of(this).get(MainViewModel.class);
mainViewModel.usersList.observe(this,pagedList-> playerAdapter.submitList(pagedList));

sample code for pagedListAdapter is given below

public class PlayerAdapter extends PagedListAdapter<Player,PlayerAdapter.PlayerViewHolder>
{
Context context;

public PlayerAdapter(Context context)
{
super(DIFF_CALLBACK);
this.context=context;
}

@Override
public PlayerViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.from(context).inflate(R.layout.item_player_row, parent, false);
return new PlayerViewHolder(itemView);
}

@Override
public void onBindViewHolder(@NonNull PlayerViewHolder holder, int position)
{
Player player = getItem(position);

if (player != null)
{
holder.bindTo(player);
} else {
holder.clear();
}
}



public static final DiffUtil.ItemCallback<Player> DIFF_CALLBACK = new DiffUtil.ItemCallback<Player>()
{
@Override
public boolean areItemsTheSame(
@NonNull Player oldUser, @NonNull Player newUser)
{
// User properties may have changed if reloaded from the DB, but ID is fixed
return oldUser.getPid() == newUser.getPid();
}
@Override
public boolean areContentsTheSame(
@NonNull Player oldUser, @NonNull Player newUser)
{
// NOTE: if you use equals, your object must properly override Object#equals()
// Incorrectly returning false here will result in too many animations.
return oldUser.equals(newUser);
}
};


public class PlayerViewHolder extends RecyclerView.ViewHolder
{
ImageView ivProfile;
TextView tvName,tvBattingStyle,tvRole,tvBowlingStyle,tvMajorTeams;

public PlayerViewHolder(View itemView)
{
super(itemView);
ivProfile=(ImageView)itemView.findViewById(R.id.ivProfile);
tvBattingStyle=(TextView) itemView.findViewById(R.id.tvBattingStyle);
tvBowlingStyle=(TextView) itemView.findViewById(R.id.tvBowlingStyle);
tvMajorTeams=(TextView) itemView.findViewById(R.id.tvMajerTeams);
tvRole=(TextView) itemView.findViewById(R.id.tvRole);
tvName=(TextView) itemView.findViewById(R.id.tvName);

}

void bindTo(Player player)
{
itemView.setTag(player.getPid());
Glide.with(itemView.getContext()).load(player.getImageURL()).into(ivProfile);
tvName.setText(player.getFullName());
tvBattingStyle.setText(player.getBattingStyle());
tvRole.setText(player.getPlayingRole());
tvBowlingStyle.setText(player.getBowlingStyle());
tvMajorTeams.setText(player.getMajorTeams());
}

void clear()
{
itemView.invalidate();
ivProfile.invalidate();
tvRole.invalidate();
tvName.invalidate();
tvMajorTeams.invalidate();
tvBattingStyle.invalidate();
tvBowlingStyle.invalidate();
}
}
}

If you are getting the data from the database means live data with RoomDb is preferable way.

We will see data binding,Paging with Network Call and Navigation Library of JetPack in NextPost.

The sample source code for paging library in java is in github https://github.com/arunpandian22/PagingLibrarySampleJava