Android Architecture Components: Working with Room Persistence Library
What is Android Architecture Components?
> Architecture components are a set of Android libraries that help you structure your app in a way that is robust, testable, and maintainable.
What is Room Persistence Library?
> Google introduce Room Persistence Library in Google I/O 2017. Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. There are several problems we faced when we used core framework with SQLite.
1). No compile time validation of query against the schema.so that broken SQL queries result in compile time errors, instead of runtime failures.
2). When database contains large amount of data it generates issues that can negatively impact the performance of apps, such as accessing persistent storage from the main thread.
3). As your schema changes, you need to update the affected SQL queries manually. This process can be time consuming and error prone.
New Room Persistence Library takes care of these concerns for you.
There are 3 major components in Room Persistence Library:
- Database: Contains the database holder and serves as the main access point for the underlying connection to your app’s persisted, relational data.
- Entity: Represents a table within the database.
- DAO: Contains the methods used for accessing the database.
Some important Annotations in Room Persistence Library
- @Entity : Create a SQLite table in database using a data model class.
- @Dao : Create a Data Access Object in the Database using a interface class.
- @Database : A class with this annotation will create an abstraction for the Data Access Object.
- @PrimaryKey : A variable with this annotation will set a primary key for the table
- @Insert : Inserts parameters into the table
- @Update : Updates parameters of an existing table.
- @Delete : Deletes parameters of an existing table.
- @Query : Running SQL query method within the table.
- @Ignore : Ignores the parameter from the Room database.
Now Let’s start with the example
Implementation
How to integrate Room Persistence Library into Android Project?
Step 1:
Add the gradle dependencies in the build.gradle file.
dependencies {
def room_version = "1.1.1"
implementation "android.arch.persistence.room:runtime:$room_version"
kapt "android.arch.persistence.room:compiler:$room_version" //use kapt if you are using kotlin otherwise use implementation
// Test helpers
testImplementation "android.arch.persistence.room:testing:$room_version"
}
Add “kotlin-kapt” plugin on the top of the build.gradle if you are going to use kotlin language.
apply plugin: 'kotlin-kapt'
Step 2:
Now we are going to make User Table Using Entity Annotation.
package com.android4dev.roomjetpack.db.entity
import android.arch.persistence.room.Entity
import android.arch.persistence.room.ColumnInfo
import android.arch.persistence.room.PrimaryKey
/***
* Android4Dev
*/
@Entity(tableName = "user")
class User(
//For autoincrement primary key
@PrimaryKey(autoGenerate = true)
var uid: Int = 0,
@ColumnInfo(name = "first_name")
var firstName: String? = null,
@ColumnInfo(name = "last_name")
var lastName: String? = null
) {
constructor() : this(0, "", "")
}
Step 3:
Now create Data Access Object using interface. We can use @Query, @Insert, @Update, @Delete annotation for CRUD Operation.
package com.android4dev.roomjetpack.db.dao
import android.arch.persistence.room.Dao
import android.arch.persistence.room.Delete
import android.arch.persistence.room.Insert
import android.arch.persistence.room.Query
import com.android4dev.roomjetpack.db.entity.User
/***
* Android4Dev
*/
@Dao
interface UserDAO {
@Query("SELECT * FROM user")
fun getAll(): List<User>
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
fun loadAllByIds(userIds: IntArray): List<User>
@Query("SELECT * FROM user WHERE first_name LIKE :first AND " + "last_name LIKE :last LIMIT 1")
fun findByName(first: String, last: String): User
@Insert
fun insertAll(vararg users: User)
@Delete
fun delete(user: User)
}
Tip: When inserting data, you can provide a conflict strategy.
In this example, you do not need a conflict strategy, because the uid is your primary key, and the default SQL behavior is ABORT, so that you cannot insert two items with the same primary key into the database.
If the table has more than one column, you can use
@Insert(onConflict = OnConflictStrategy.REPLACE)
to replace a row.
Step 4:
Now create database using @Database annotation. Now create one class and extend it using RoomDatabase. It is good practice to use singleton approach. Because RoomDatabase class is very heavy.
package com.android4dev.roomjetpack.db
import android.arch.persistence.room.Database
import android.arch.persistence.room.Room
import android.arch.persistence.room.RoomDatabase
import android.content.Context
import com.android4dev.roomjetpack.db.dao.UserDAO
import com.android4dev.roomjetpack.db.entity.User
/***
* Android4Dev
*/
@Database(entities = [(User::class)], version = 1)
abstract class ApplicationDatabase : RoomDatabase() {
//Generate Singleton Instance
companion object {
private var INSTANCE: ApplicationDatabase? = null
fun getInstance(context: Context): ApplicationDatabase {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context,
ApplicationDatabase::class.java, "android4dev.db").allowMainThreadQueries().build()
}
return INSTANCE!!
}
}
abstract fun userDao(): UserDAO
}
allowMainThreadQueries() is not a good practice. It is always advisable to run database operation on separate thread. allowMainThreadQueries() this will allow to run database operation on main thread.
Step 5:
Now create one xml file called activity_main.xml and add below code.
<?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=".MainActivity">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:fontFamily="sans-serif"
android:text="Android4Dev"
android:textColor="@color/colorPrimaryDark"
android:textSize="22sp"
android:textStyle="bold"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/editFirstName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:hint="Enter First Name"
android:inputType="text"
android:textColor="@android:color/black"
android:textSize="14sp"
app:layout_constraintTop_toBottomOf="@id/title" />
<EditText
android:id="@+id/editLastName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:hint="Enter First Name"
android:inputType="text"
android:textColor="@android:color/black"
android:textSize="14sp"
app:layout_constraintTop_toBottomOf="@id/editFirstName" />
<Button
android:id="@+id/buttonAdd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add"
android:textSize="16sp"
app:layout_constraintTop_toBottomOf="@id/editLastName" />
<Button
android:id="@+id/buttonList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="User List"
android:textSize="16sp"
app:layout_constraintTop_toBottomOf="@id/buttonAdd" />
</android.support.constraint.ConstraintLayout>
Step 6:
Now create one activity name as MainActivity.kt
package com.android4dev.roomjetpack
import android.content.Intent
import android.os.AsyncTask
import android.os.Bundle
import android.view.View
import android.widget.Toast
import com.android4dev.roomjetpack.appinterface.AsyncResponseCallback
import com.android4dev.roomjetpack.base.BaseActivity
import com.android4dev.roomjetpack.db.ApplicationDatabase
import com.android4dev.roomjetpack.db.dao.UserDAO
import com.android4dev.roomjetpack.db.entity.User
import com.android4dev.roomjetpack.db.helper.RoomConstants
import kotlinx.android.synthetic.main.activity_main.*
/***
* Android4Dev
*/
class MainActivity : BaseActivity(), View.OnClickListener, AsyncResponseCallback {
private var db: ApplicationDatabase? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initView()
}
private fun initView() {
db = ApplicationDatabase.getInstance(this)
buttonAdd.setOnClickListener(this)
buttonList.setOnClickListener(this)
}
override fun onClick(clickView: View?) {
when (clickView?.id) {
R.id.buttonAdd -> {
val user = User(firstName = editFirstName.text.toString(), lastName = editLastName.text.toString().trim())
InsertUserAsync(db!!.userDao(), RoomConstants.INSERT_USER, this).execute(user)
}
R.id.buttonList -> {
val intent = Intent(this, UserListActivity::class.java)
startActivity(intent)
}
}
}
override fun onResponse(isSuccess: Boolean, call: String) {
if (call == RoomConstants.INSERT_USER) {
if (isSuccess) {
editFirstName.text.clear()
editLastName.text.clear()
Toast.makeText(this@MainActivity, "Successfully added", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@MainActivity, "Some error occur please try again later!!!", Toast.LENGTH_SHORT).show()
}
}
}
class InsertUserAsync(private val userDao: UserDAO, private val call: String, private val responseAsyncResponse: AsyncResponseCallback) : AsyncTask<User, Void, User>() {
override fun doInBackground(vararg user: User?): User? {
return try {
userDao.insertAll(user[0]!!)
user[0]!!
} catch (ex: Exception) {
null
}
}
override fun onPostExecute(result: User?) {
super.onPostExecute(result)
if (result != null) {
responseAsyncResponse.onResponse(true, call)
} else {
responseAsyncResponse.onResponse(false, call)
}
}
}
}
As you can see in code we can create database instance using below code.
db = ApplicationDatabase.getInstance(this)
We have created this AsyncTask For inserting data into table. AsyncTask will run database operation in separate thread.
class InsertUserAsync(private val userDao: UserDAO, private val call: String, private val responseAsyncResponse: AsyncResponseCallback) : AsyncTask<User, Void, User>() {
override fun doInBackground(vararg user: User?): User? {
return try {
userDao.insertAll(user[0]!!)
user[0]!!
} catch (ex: Exception) {
null
}
}
override fun onPostExecute(result: User?) {
super.onPostExecute(result)
if (result != null) {
responseAsyncResponse.onResponse(true, call)
} else {
responseAsyncResponse.onResponse(false, call)
}
}
}
Step 7:
Now create another xml file for display list of users. Name this xml file as activity_user_list.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=".UserListActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerUserList"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.constraint.ConstraintLayout>
Step 8:
Create another xml file called list_item_user.xml and add below code.
<?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"
xmlns:tool="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp">
<TextView
android:id="@+id/textNameTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:text="Name: "
android:textSize="14sp" />
<TextView
android:id="@+id/textName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:textSize="14sp"
app:layout_constraintStart_toEndOf="@id/textNameTitle"
tool:text="Lokesh" />
<TextView
android:id="@+id/textCountryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:fontFamily="sans-serif"
android:text="Country: "
android:textSize="14sp"
app:layout_constraintTop_toBottomOf="@id/textNameTitle" />
<TextView
android:id="@+id/textCountry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:fontFamily="sans-serif"
android:textSize="14sp"
app:layout_constraintStart_toEndOf="@id/textCountryTitle"
app:layout_constraintTop_toBottomOf="@id/textNameTitle"
tool:text="India" />
<TextView
android:id="@+id/textDelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="10dp"
android:fontFamily="sans-serif"
android:padding="5dp"
android:text="Delete"
android:textColor="@android:color/holo_red_light"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@id/textCountryTitle" />
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>
Step 9:
Now create one Activity called as UserListActivity.kt and add below code.
package com.android4dev.roomjetpack
import android.os.AsyncTask
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.widget.Toast
import com.android4dev.roomjetpack.adapter.UserListAdapter
import com.android4dev.roomjetpack.appinterface.AsyncResponseCallback
import com.android4dev.roomjetpack.base.BaseActivity
import com.android4dev.roomjetpack.db.ApplicationDatabase
import com.android4dev.roomjetpack.db.dao.UserDAO
import com.android4dev.roomjetpack.db.entity.User
import com.android4dev.roomjetpack.db.helper.RoomConstants
import kotlinx.android.synthetic.main.activity_user_list.*
class UserListActivity : BaseActivity(), AsyncResponseCallback {
private lateinit var userListAdapter: UserListAdapter
private var db: ApplicationDatabase? = null
private lateinit var arrayUser: List<User>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user_list)
initView()
}
private fun initView() {
db = ApplicationDatabase.getInstance(this)
recyclerUserList.layoutManager = LinearLayoutManager(this)
userListAdapter = UserListAdapter()
userListAdapter.onItemDeleteClick = { position ->
DeleteUserAsync(db!!.userDao(), RoomConstants.DELETE_USER, this).execute(arrayUser[position])
}
arrayUser = db?.userDao()?.getAll()!!
userListAdapter.setUserList(arrayUser.toMutableList())
recyclerUserList.adapter = userListAdapter
}
override fun onResponse(isSuccess: Boolean, call: String) {
if (call == RoomConstants.DELETE_USER) {
if (isSuccess) {
arrayUser = db?.userDao()?.getAll()!!
userListAdapter.setUserList(arrayUser.toMutableList())
userListAdapter.notifyDataSetChanged()
Toast.makeText(this@UserListActivity, "Successfully deleted", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@UserListActivity, "Some error occur please try again later!!!", Toast.LENGTH_SHORT).show()
}
}
}
class DeleteUserAsync(private val userDao: UserDAO, private val call: String, private val responseAsyncResponse: AsyncResponseCallback) : AsyncTask<User, Void, User>() {
override fun doInBackground(vararg user: User?): User? {
return try {
userDao.delete(user[0]!!)
user[0]!!
} catch (ex: Exception) {
null
}
}
override fun onPostExecute(result: User?) {
super.onPostExecute(result)
if (result != null) {
responseAsyncResponse.onResponse(true, call)
} else {
responseAsyncResponse.onResponse(false, call)
}
}
}
}
For getting all user from database we will use
db?.userDao()?.getAll()!!
Below class will use for delete user entry. Again we have created AsyncTask for deleting user data.
class DeleteUserAsync(private val userDao: UserDAO, private val call: String, private val responseAsyncResponse: AsyncResponseCallback) : AsyncTask<User, Void, User>() {
override fun doInBackground(vararg user: User?): User? {
return try {
userDao.delete(user[0]!!)
user[0]!!
} catch (ex: Exception) {
null
}
}
override fun onPostExecute(result: User?) {
super.onPostExecute(result)
if (result != null) {
responseAsyncResponse.onResponse(true, call)
} else {
responseAsyncResponse.onResponse(false, call)
}
}
}
Step 10:
Create one constant file for RoomDatabase Operation.
package com.android4dev.roomjetpack.db.helper
class RoomConstants {
companion object {
const val INSERT_USER="INSERT_USER"
const val DELETE_USER="DELETE_USER"
}
}
At the end i can just say that Room Persistence Library provides many features i can’t explain all features in one blog this is basic Database Operation which i have explained in this blog.
Hope you will like this blog if you have any question please ask me in comments or on my twitter, facebook account.
Source Code
Demo Link For Room Persistence Library

Lokesh Desai
Related posts
Bottom Sheet Android Kotlin
Subscribe
* You will receive the latest news and updates on your favorite celebrities!
Quick Cook!
How to use Data Class in Kotlin?
In this blog, you will learn about How to use Data Class in Kotlin? and What is Data Class? Tired…
Android Kotlin Tutorial: How to create a Class and Object ?
Today we are going to learn How to Create a Class and Object in Kotlin. Kotlin is an Object Oriented…