Paging Library is one of the main parts of the complete set of the Architecture 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 data from Database.
Paging Library includes the DataSource, PagedList, PageListBuilder and PagedListAdapter. We will go through each component one by one. But first of all, What is paging Library?
Paging Library is a set of the component which helps app developer to load the data from backed gradually as it required. For example, an app loads thousands of line from the database or network but that all data can’t be display at once to the user. Just a small portion of all those data will be displayed to the user at a time. But if the app goes to load all these data at once then it creates the huge burden on the network and slows down the app as well. If we load the data on load more button click than user have to wait till data is not loaded from the database or network.
Currently, a developer has to use lots of terminologies to efficiently handle all these performance issues and bad user experience. But using the Google Architecture Components Paging Library we can do this work very efficiently without quickly.
DataSource is the main component which loads the chunk of pages into the PagedList. Data loaded by the PagedList can’t be updated but it can grow as required. In case loaded data need to be updated than a new PagedList and DataSource pair is required. But you don’t have to worry about to provide each and every time new set of PagedList and DataSource pair, as its all are auto-managed by architecture components Library.
PagedList is responsible for query the DataSource and loads the data from DataSource. When we create the PagedList it immediately loads the data from DataSource. Later on when loadAruond() method is called then its load the data from DataSource.
The behaviour of the data loading is controlled by PagedList.Config, using this we can set the page size and prefetch distance.
PagedListAdapter is a RecyclerView.Adapter which is capable of showing the paged data into RecyclerView. It accepts LiveData Paged List as input. For setting the data to this PagedListAdapter we can also user setList(PagedList) method.
This LivePagedLIstBuilder is used to create the PagedList. We have to provide a config to the constructor of the LivedPagedListBuilder, using this config we can change or customize the behaviour data loading from DataSource.
In above sample, we have provided the list of all TODO item by DAO and PagedList Config.
We will use the almost all components of the Architecture Components in this TODO app.In this demo app, we will add the todo item and save into the SQLite database and use the sweep to delete option to delete the item. Our ViewModel, LiveData, RoomDatabase and Paging will work together and update the UI automatically in real time.
We required two layouts for this demo app. First is the main layout file of the App and second is the item view to show the Todo Item into the RecyclerView.
activity_main.xml
todo_item.xml
Below Library is required to create this ToDo app demo. So, add these libraries to your project level build.gradle file.
For this demo app, we only required two fields, Id and name of the todo. Below is the code of our Entity class.
Todo.java
This interface is used for the accessing the data stored in the SQLite database.
TodoDao.java
Next, we need our SQLite database class.
TodoDB.java
You may have to add the schema details in your compilation script and database migration functionality as well. If not familiar with room database and don't know how to do then check the RoomDatabase Tutorial.
TodoViewHolder.java
TodoViewModel.java
This is the main component which is used to show the Livedata<PagedList> data into RecyclearView and trigger to load the data from DataSource using PagedList.
TodoAdapter.java
This PagedListAdapter uses the constructor and takes the DiffUtil callbacks as input. This DiffUtil help RecyclerView to load the latest received PagedList data. Below is the code of the DiffUtil.
All the above class and code is just helper class for our main class. Now, we will create the MainActivity.java in which we will bind all these class together.
In our main activity class, we will create the instance of the RoomDatabase using Room Database Builder. This RoomDatabase Builder class takes three parameters as shown below.
Get the DAO using database instance.
Create the instance of the TodoViewModel using code below.
.To create the LivePagedList using the builder. We have to call viewModel.init(todoDao) method from our main activity.
Create the instance of the PagedListAdapter class using the code below.
Put the observer on the ViewModel todoList to update the UI with latest PagedList.
This method is used for deleting the item from the Database using Dao interface.
Create the addTodo method as given below. This method does the following work.
RoomDatabase does not allow you to run DB query on the main thread. So below given insert and remove method is the respective method to delete and remove the ToDo item form SQLite database using a thread executor.
Now, create a method to add the listener on our add Todo button to call the addTodo() method.
This Article is very basic or we can say a model for implementing the ViewModel, LiveData, RoomDatabase and Paging Library Architectural components together. If you go step by step then its very easy to implement.
You can download the source code of this Tutorial form NPLIX Repository on GitHub.
Architecture Components Paging Library
Paging Library includes the DataSource, PagedList, PageListBuilder and PagedListAdapter. We will go through each component one by one. But first of all, What is paging Library?
Paging Library is a set of the component which helps app developer to load the data from backed gradually as it required. For example, an app loads thousands of line from the database or network but that all data can’t be display at once to the user. Just a small portion of all those data will be displayed to the user at a time. But if the app goes to load all these data at once then it creates the huge burden on the network and slows down the app as well. If we load the data on load more button click than user have to wait till data is not loaded from the database or network.
Currently, a developer has to use lots of terminologies to efficiently handle all these performance issues and bad user experience. But using the Google Architecture Components Paging Library we can do this work very efficiently without quickly.
What is DataSource in Paging Library?
DataSource is the main component which loads the chunk of pages into the PagedList. Data loaded by the PagedList can’t be updated but it can grow as required. In case loaded data need to be updated than a new PagedList and DataSource pair is required. But you don’t have to worry about to provide each and every time new set of PagedList and DataSource pair, as its all are auto-managed by architecture components Library.
@Query("SELECT * FROM Todo ORDER BY todo COLLATE NOCASE ASC")
public abstract DataSource.Factory<Integer, Todo> allTodoByName();
PagedList
PagedList is responsible for query the DataSource and loads the data from DataSource. When we create the PagedList it immediately loads the data from DataSource. Later on when loadAruond() method is called then its load the data from DataSource.
The behaviour of the data loading is controlled by PagedList.Config, using this we can set the page size and prefetch distance.
PagedList.Config pagedListConfig=(new PagedList.Config.Builder()).setEnablePlaceholders(true)
.setPrefetchDistance(10)
.setPageSize(20).build();
PagedListAdapter
PagedListAdapter is a RecyclerView.Adapter which is capable of showing the paged data into RecyclerView. It accepts LiveData Paged List as input. For setting the data to this PagedListAdapter we can also user setList(PagedList) method.
public class TodoAdapter extends PagedListAdapter<Todo,TodoViewHolder> {
LivePagedListBuilder
This LivePagedLIstBuilder is used to create the PagedList. We have to provide a config to the constructor of the LivedPagedListBuilder, using this config we can change or customize the behaviour data loading from DataSource.
LiveData<PagedList<Todo>> todoList = new LivePagedListBuilder<>(
todoDao.allTodoByName(), pagedListConfig).build();
}
In above sample, we have provided the list of all TODO item by DAO and PagedList Config.
Description of the TODO app using Paging Library
We will use the almost all components of the Architecture Components in this TODO app.In this demo app, we will add the todo item and save into the SQLite database and use the sweep to delete option to delete the item. Our ViewModel, LiveData, RoomDatabase and Paging will work together and update the UI automatically in real time.
Create Layout for Todo App
We required two layouts for this demo app. First is the main layout file of the App and second is the item view to show the Todo Item into the RecyclerView.
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.pagingdemo.MainActivity">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/inputText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/add_todo"
android:imeOptions="actionDone"
android:inputType="text"
android:maxLines="1" />
<Button
android:id="@+id/addButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:text="@string/add" />
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/todoList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:visibility="visible"
app:layoutManager="LinearLayoutManager"
app:layout_constraintTop_toBottomOf="@+id/linearLayout" />
</android.support.constraint.ConstraintLayout>
todo_item.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:cardUseCompatPadding="true">
<TextView android:id="@+id/name"
style="@style/TextAppearance.AppCompat.Medium"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.v7.widget.CardView>
Required Library for Todo App
Below Library is required to create this ToDo app demo. So, add these libraries to your project level build.gradle file.
compile 'com.android.support:cardview-v7:26.1.0'
compile 'com.android.support:recyclerview-v7:26.1.0'
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:1.1.0"
annotationProcessor "android.arch.lifecycle:compiler:1.1.0"
// Room
implementation "android.arch.persistence.room:runtime:1.0.0"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0"
// Paging
implementation "android.arch.paging:runtime:1.0.0-alpha5"
Create RoomDatabase Classes for Paging Library Demo
For this demo app, we only required two fields, Id and name of the todo. Below is the code of our Entity class.
Todo.java
@Entity
public class Todo {
@PrimaryKey(autoGenerate = true)
int id;
public String getTodo() {
return todo;
}
public void setTodo(String todo) {
this.todo = todo;
}
private String todo;
}
Data Access Object for Paging Library
This interface is used for the accessing the data stored in the SQLite database.
TodoDao.java
@Dao
public interface TodoDao {
@Query("SELECT * FROM Todo ORDER BY todo COLLATE NOCASE ASC")
public abstract DataSource.Factory<Integer, Todo> allTodoByName();
@Insert
public void insert(List<Todo> todos);
@Insert
public void insert(Todo todo);
@Delete
public void delete(Todo todo);
}
Next, we need our SQLite database class.
TodoDB.java
@Database(entities = Todo.class,version = 1)
public abstract class TodoDB extends RoomDatabase {
public abstract TodoDao TodoDao();
}
You may have to add the schema details in your compilation script and database migration functionality as well. If not familiar with room database and don't know how to do then check the RoomDatabase Tutorial.
Create ViewHolder and ViewModel
TodoViewHolder.java
class TodoViewHolder extends RecyclerView.ViewHolder{
private TextView todoName;
public Todo todo;
public TodoViewHolder(View itemView) {
super(itemView);
todoName= itemView.findViewById(R.id.name);
}
void bindTo(Todo todo){
this.todo=todo;
todoName.setText(todo.getTodo());
}
}
TodoViewModel.java
public class TodoViewModel extends ViewModel {
LiveData<PagedList<Todo>> todoList;
public TodoViewModel() {
}
public void init(TodoDao todoDao){
PagedList.Config pagedListConfig=(new PagedList.Config.Builder()).setEnablePlaceholders(true)
.setPrefetchDistance(10)
.setPageSize(20).build();
todoList = new LivePagedListBuilder<>(
todoDao.allTodoByName(), pagedListConfig).build();
}
}
PagedListAdapter to Load the PagedList.
This is the main component which is used to show the Livedata<PagedList> data into RecyclearView and trigger to load the data from DataSource using PagedList.
TodoAdapter.java
public class TodoAdapter extends PagedListAdapter<Todo,TodoViewHolder> {
protected TodoAdapter(@NonNull DiffCallback diffCallback) {
super(diffCallback);
}
@Override
public TodoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new TodoViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.todo_item, parent, false));
}
@Override
public void onBindViewHolder(TodoViewHolder holder, int position) {
holder.bindTo(getItem(position));
}
}
This PagedListAdapter uses the constructor and takes the DiffUtil callbacks as input. This DiffUtil help RecyclerView to load the latest received PagedList data. Below is the code of the DiffUtil.
private DiffCallback diffCallback = new DiffCallback<Todo>() {
@Override
public boolean areItemsTheSame(@NonNull Todo oldItem, @NonNull Todo newItem) {
return oldItem.id == newItem.id;
}
@Override
public boolean areContentsTheSame(@NonNull Todo oldItem, @NonNull Todo newItem) {
return oldItem == newItem;
}
};
All the above class and code is just helper class for our main class. Now, we will create the MainActivity.java in which we will bind all these class together.
Create Instance of RoomDatabase
In our main activity class, we will create the instance of the RoomDatabase using Room Database Builder. This RoomDatabase Builder class takes three parameters as shown below.
- Context is the first parameter
- Class name of the RoomDatabase in our case its TodoDB.class
- Third and last is the name of the Database, In our case its MyTodoDB
todoDB= Room.databaseBuilder(getApplicationContext(),TodoDB.class,"MyTodoDB")
.addCallback(new RoomDatabase.Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
}
}).build();
Get the DAO using database instance.
todoDao=todoDB.TodoDao();
Create the instance of the TodoViewModel using code below.
viewModel = ViewModelProviders.of(this).get(TodoViewModel.class);
viewModel.init(todoDao);
.To create the LivePagedList using the builder. We have to call viewModel.init(todoDao) method from our main activity.
Create the instance of the PagedListAdapter class using the code below.
TodoAdapter todoAdapter=new TodoAdapter(diffCallback);
Put the observer on the ViewModel todoList to update the UI with latest PagedList.
viewModel.todoList.observe(this, todoAdapter::setList);
Create Method to Sweep to Delete
This method is used for deleting the item from the Database using Dao interface.
public void initSwipeToDelete(){
new ItemTouchHelper(new ItemTouchHelper.Callback() {
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
return makeMovementFlags(0,ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
remove(((TodoViewHolder) viewHolder).todo);
}
}).attachToRecyclerView(todoList );
}
Create the addTodo method as given below. This method does the following work.
- Get the ToDo name from editText
- Insert the todo item into the database using insert() method
- Then, at last, clear the edit box for next user input
private void addTodo() {
String newTodo = inputText.getText().toString().trim();
if (newTodo!=null) {
insert(newTodo);
inputText.setText("");
}
}
RoomDatabase does not allow you to run DB query on the main thread. So below given insert and remove method is the respective method to delete and remove the ToDo item form SQLite database using a thread executor.
public void insert(CharSequence text){
final Todo todo=new Todo();
todo.setTodo(text.toString());
IO_EXECUTOR.execute(()->{
todoDB.TodoDao().insert(todo);
});
}
public void remove(Todo todo){
IO_EXECUTOR.execute(()->{
todoDB.TodoDao().delete(todo);
});
}
Now, create a method to add the listener on our add Todo button to call the addTodo() method.
private void initAddButtonListener(){
addBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addTodo();
}
});
inputText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if(actionId== EditorInfo.IME_ACTION_DONE){
addTodo();
}
return true;
}
});
inputText.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if(event.getAction()== KeyEvent.ACTION_DOWN && keyCode== KeyEvent.KEYCODE_ENTER){
addTodo();
}
return true;
}
});
}
Conclusion
This Article is very basic or we can say a model for implementing the ViewModel, LiveData, RoomDatabase and Paging Library Architectural components together. If you go step by step then its very easy to implement.
You can download the source code of this Tutorial form NPLIX Repository on GitHub.
[…] Google Architecture Component for using the pagination of the resource. It is helpful to create the pagination from Database using Room as well as with network resource. Here in this article, we will explore the how to use it for […]
ReplyDelete