--

Showing posts with label RoomDB. Show all posts
Showing posts with label RoomDB. Show all posts

Wednesday, 24 October 2018

Chapter 1 : Modern Android Architecture ‘Android Jetpack’ – Managing & Tracking Android Lifecycle (Room, LiveData and ViewModel)


        While managing lifecycle, we have to manage the data, orientations or memory leaks all based on lifecycle and we have to do it on our own which is cumbersome sometimes. So, we need these new set of libraries which can do most of our work very easily without going into deep. The Support Library components aren’t the only familiar features in the list of Jetpack components. All of the Architecture Components (Lifecycle, LiveData, Room and ViewModel) are now part of Jetpack, too.

It helps us to solve two critical points:

  • Manage our UI components lifecycle
  • Persist data over configuration changes

In this chapter, We are going to develop an app, which basically
  1. Takes some input from the user(LiveData)
  2. Save into the local database(Room) 
  3. Show it on the screen(ViewModel)
Let’s get started!!

Note : Use Android Studio 3.0 or later version.

Adding Dependencies (Pre AndroidX)


Room is basically a Database object mapping library use to access the database. Lifecycle, this has some good set of classes like ViewModel and LiveData which we will use to manage the lifecycle of our app. Add these libraries to build.gradle (Module: app) file, at the end of the dependencies block.

dependencies {
// Room components
implementation 'android.arch.persistence.room:runtime:2.0.0'
annotationProcessor 'android.arch.persistence.room:compiler:2.0.0'

// Lifecycle components
implementation 'android.arch.lifecycle:extensions:2.0.0'
annotationProcessor 'android.arch.lifecycle:compiler:2.0.0'
}

Refer here for AndroidX Dependencies.


Room (Local database)

We can query your data without having to deal with cursors or loaders. We can define our database by adding annotations in your Model class. So get rid of third-party ORMs like Sugar, In fact, when the official Android libraries give you an equal, or if not, better solution. The library helps you create a cache of your app's data on a device that's running your app. If user does not have internet connection, Just chill ! No problem! This cache allows users to view a consistent copy of key information within your app.

3 key concepts of Room


@Entity - We just have to annotate “@Entity” to a class and the name of the class becomes table name and, data members becomes the name of the columns. “@Entity” class represent an entity in a table.



Here, we have class StudentModel, and name of table is also same. We had made a column itemName, personName, and addedDate.

@Dao (Data Access Object) - An Interface where we put all our SQL queries. No need to write whole quires now, we just need to make a method and annotate with specific annotations like “@Insert”, “@Delete”, “@Query(SELECT FROM *)”



Here, we have an interface StudentModelDao. To insert the data we annotated “@Insert” to insert method. Room doesn’t gives us annotations which can help us in selecting everything so we have “@Query” to do some custom queries.

@Database - We need to create an abstract class (Room class) which extends RoomDatabase. It is a database layer over the SQLite database; this helps us in all the work which we use to do in SQLiteOpenHelper class. We need only a single instance for the whole app.



Here, we have a Room class AppRoomDatabase in which we declare all our entities and version of the database. getDatabase() method will return the room database instance. If we want to access the database lets deep dive into ViewModel and LiveData.


ViewModel (Data-handling business logic)

ViewModel is the new class provided by lifecycle. It can be termed as the bridge between model and UI but quite intelligent one in the sense that it can be automatically retained in the case of orientation change .

Generally what happens during orientation change?
        For instance we are in one activity and the activity loads some list and add it to the RecyclerView. Now when the orientation change has not been handled then new instance of activity will be created and the loading of list takes place and is then again added to RecyclerView.

How does 
ViewModel solve this problem?
        If you have used 
ViewModel along with LiveData class to store data then during orientation change new instance of activity will be created but the data won't be downloaded again. ViewModel will provide the most recent available data.

Note: Don’t think that 
ViewModel will hold data forever or for every case. If you close or activity is destroyed the ViewModel will also be destroyed or cleared.



StudentListViewModel class must extend the ViewModel class. If the ViewModel needs the application context, then it must extend the AndroidViewModel class. 
In our ViewModel, we first get an instance of our database using AppRoomDatabase.getDatabase(this.getApplication())

We need to load the list of student items from the database. For that, we should use the query we defined in the DAO class, getAllStudItems(). Next, call the abstract method we created for DAO and then call the query method. 


appDatabase.itemAndStudentModel().getAllStudItems();

The ViewModel will contain all the data needed for our Activity. In our example, we are using something called LiveData.

LiveData

LiveData is an observable data holder. It can only be observed in the context of a lifecycle, more precisely in the context of an Activity or Fragment lifecycle. By passing the reference of an Activity or Fragment, it can understand whether your UI onScreen, offScreen or Destroyed. After passing the UI object to LiveData, whenever the data in the live data changes. It notifies the lifecycle owner with updates and then the UI redraw itself with updates.

MutableLiveData extends LiveData internally and also the two methods of LiveData available,
1. setValue() : Set the value and dispatch the value to all the active observers. It cannot be done in background thread it must be done in the main thread only.
2. postValue() : Post a task to main thread to override value set by setvalue. As setvalue cannot be called from background thread so post value must be used to set value from background thread.


How LiveData solved developer`s major Headaches ?

  • No more manual life-cycling handle - Observers just observe relevant data and don’t stop or resume observation. LiveData manages all of this under control.
  • Proper configuration changes - If an observer is recreated due to a configuration change, like device rotation, it immediately receives the latest available data.
  • Ensures your UI matches the data state -Instead of updating the UI every-time when the data changes, your observer can update the UI every time there’s a change. It receives the latest data upon becoming active again.
  • No memory leaks - Observers are bound to Lifecycle objects and clean up after themselves when their associated life cycle destroyed.
  • No crashes due to stopped activities - It means if an activity is in the back stack, then it doesn’t receive any LiveData stream.
  • Sharing resources - You can extend LiveData object using the singleton pattern to wrap system services so that they can be shared in your app.
  • We wrap our list of student items inside LiveData so that the Activity can observe changes in the data and update the UI.


Framing RecyclerView Adapter

Now since we will be displaying a list of student items, we need a RecyclerView. So first, let’s create an adapter for the same.

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder> {
private List<StudentModel> studentModelList;
private View.OnLongClickListener longClickListener;

public RecyclerViewAdapter(List<StudentModel> studentModelList, View.OnLongClickListener longClickListener) {
this.studentModelList = studentModelList;
this.longClickListener = longClickListener;
}

@Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new RecyclerViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_item, parent, false));
}

@Override
public void onBindViewHolder(final RecyclerViewHolder holder, int position) {
StudentModel borrowModel = studentModelList.get(position);
holder.itemTextView.setText(borrowModel.getItemName());
holder.nameTextView.setText(borrowModel.getPersonName());
holder.dateTextView.setText(borrowModel.getAddedDate().toLocaleString().substring(0, 11));
holder.itemView.setTag(borrowModel);
holder.itemView.setOnLongClickListener(longClickListener);
}

@Override
public int getItemCount() {
return studentModelList.size();
}

public void addItems(List<StudentModel> borrowModelList) {
this.studentModelList = borrowModelList;
notifyDataSetChanged();
}

static class RecyclerViewHolder extends RecyclerView.ViewHolder {
private TextView itemTextView;
private TextView nameTextView;
private TextView dateTextView;
RecyclerViewHolder(View view) {
super(view);
itemTextView = view.findViewById(R.id.itemTextView);
nameTextView = view.findViewById(R.id.nameTextView);
dateTextView = view.findViewById(R.id.dateTextView);
}
}
}


Creating the Android LifecycleObserver & LifecycleOwner


The basic idea here is that there is a class named LifecycleOwner which emits various lifecycle events (such as ON_CREATE, ON_PAUSE, etc) which you as an Android Developer are already familiar with. Now you can implement an interface name LifecycleObserver which listens to these events and respond accordingly.

public class AppLifeCycleObserver implements LifecycleObserver {
private String LOG_TAG = "AppLifeCycleObserver";
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
Log.i(LOG_TAG, "onResume");
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
Log.i(LOG_TAG, "onPause");
}
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
public void onCreate() {
Log.i(LOG_TAG, "onCreate");
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart() {
Log.i(LOG_TAG, "onStart");
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onStop() {
Log.i(LOG_TAG, "onStop");
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy() {
Log.i(LOG_TAG, "onDestroy");
}
}


Adding the Observer


Observers are added to lifecycle owners via calls to the addObserver() method of owner’s Lifecycle object, a reference to which is obtained via a call to the getLifecycle() method. Edit the HomeActivity.java class file and add code to the onCreate() method to add the observer:



Creating a Lifecycle Owner

By creating a custom lifecycle owner class and demonstrate how to trigger events and modify the lifecycle state from within that class. The class is going to need a LifecycleRegistry instance initialized with a reference to itself, and a getLifecycle() method configured to return the LifecycleRegistry instance. Declare a variable to store the LifecycleRegistry reference, a constructor to initialize the LifecycleRegistry instance and add the getLifecycle() method:




Next, the class will need to notify the registry of lifecycle state changes. By triggering lifecycle events using the handleLifecycleEvent() method. For this example, we will add some methods that simply trigger lifecycle events when called. Finally change within the AppLifecycleOwner class is to add the AppLifeCycleObserver class as an observer.

In HomeActivity.java, create an instance of the AppLifecycleOwner class and to call the startLifecycleOwner() and stopLifecycleOwner() methods.



We must remove observer in onDestory() method of calling activity to avoid memory leaks.

@Override
protected void onDestroy() {
super.onDestroy();
getLifecycle().removeObserver(new AppLifeCycleObserver());
}


I have uploaded the latest source code in 
GitHub for your reference. Kindly raise your queries in the command section.

References :

https://developer.android.com/jetpack/arch/lifecycle
https://medium.com/mindorks/android-architecture-components-room-viewmodel-and-livedata-50611793e4a9
https://blog.iamsuleiman.com/android-architecture-components-tutorial-room-livedata-viewmodel/
https://codinginfinite.com/android-livedata-example/


Happy coding!!!
Cheers!!!

Thursday, 24 May 2018

Kotlin - Chapter 4 - Room DataBase framework

Saving data in local DB using Room

Room persistence library introduced at Google I/O 2017 basically is not a database it just acts as middle man between SQLite and Application layer. By caching required pieces of data from web services.
If the device cannot access the network, the user can still access that content while they are offline. Any user-initiated content changes are then synced to the server after the device is back online.
Room provides enhanced security, easy access, easy to setup and quick to get started with new database. All the DML (Data Manipulation Language) commands are now annotated except SELECT command which works with @Query.

Components of Room


We have 3 components they are
Entity : a model class annotated with @Entity where all the variable will become column name for the table and name of the model class becomes name of the table.
Database: This is an abstract class where you define all the entities that means all the tables that you want to create for that database.
Dao (Data Access Objects): This is an interface which acts is an intermediary between the user and the database. All the operation to be performed on a table has to be defined here.
Apply the kotlin-kapt plugin in the app.gradle file,
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
Add the kapt dependency in the app.gradle file,
dependencies {
   implementation fileTree(include: ['*.jar'], dir: 'libs')
   implementation "android.arch.persistence.room:runtime:$room_version"
   annotationProcessor "android.arch.persistence.room:compiler:$room_version"
   kapt "android.arch.persistence.room:compiler:$room_version"
...
}
       First of all let’s create our model class where all it’s variable name will become the column name and name of model class will become the table name.

      Now let’s create our Dao interface which contains the list of operation that we would like to perform on table.
Finally let’s create our database with version number.
@Database(entities = arrayOf(User::class), version = 1)
abstract class UserDatabase : RoomDatabase() {
abstract fun userDAOAccess(): IUserDao
}
We can insert test user to Room DB by calling insert() function,

       One great feature that Room supports is LiveData that means you can observe the changes and as soon as any data gets changed in the database you get the response. One more advantage is that LiveData observable are lifecycle aware component which gives response only when your activity or fragment is either in onStart or onResume state.
Now save and run the App. You can download the full source code for this project on GitHub
Happy coding !!!