Android Room Persistence Library in Kotlin
Welcome, here we are going to learn about the latest room in android. We are also going to use some of the newest android libraries like lifecycle.
In the earlier chapter, we have seen the implementation of SQLite Database which is a bit difficult task. But, to use the SQLite in the more natural way Google has provided us with a library called ROOM. This library is an abstraction layer over the SQLite Database. The Room is one of the components of Android Architecture Component .
First, the terminologies; there are three significant annotations for using room:
- “@Entity”
- “@Dao”
- “@Database”
a. “@Entity” Representation of table and columns become very easy; you have to annotate “@Entity” to a class and name of the class becomes table name and, data members becomes the name of the columns. “ @Entity ” class represent an entity in a table.
Example:
@Entity(tableName = “habitClass”)
data class Habit(@PrimaryKey @ColumnInfo(name = “habit”)
val mHabit: String)
Here, we have class Habit, and the name of the table is habitClass. We had made a single column habit, and for that, we used “ @ColumnInfo ” annotation and also made this a primary key by annotating “ @PrimaryKey ” to it.
b. “@Dao” — Data Access Object
An Interface where we put all our SQL queries. We don’t require to write whole queries now; we need to make a method and annotate with specific annotations like “
@Insert
”, “
@Delete
”, “
@Query(SELECT FROM
*)
”
@Dao
interface HabitDao {
@Insert
fun insert(habit: Habits)
@Query("DELETE FROM habitClass")
fun deleteAll()
@Query("SELECT * FROM habitClass ORDER BY habit ASC" )
fun getAllHabits() : List<Habits>
}
Here, we have an interface HabitDao and some methods which we will call to perform our queries. To insert the data we annotated “ @Insert ” to insert method. The room doesn’t give us annotations which can help us in deleting everything, so we have “ @Query ” to do some custom queries.
c. “@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. Room helps us with the compile-time checking of SQL statements; we need only a single instance for the whole app.
@Database(entities = arrayOf(Habit::class), version = 1)
abstract class HabitRoomDatabase : RoomDatabase{
abstract fun wordDao(): HabitDao
}
Here, we have a Room class HabitRoomDatabase in which we had to declare all our entities and version of the database. getDatabase() method will return the room database instance. If you want to modify the database do have a look at Migration .
Firstly, add room dependencies.
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
// Room dependencies
implementation 'androidx.room:room-runtime:2.0.0'
kapt 'androidx.room:room-compiler:2.0.0'
}
also, add ‘kapt’ plugin at the top of your build.gradle file
apply plugin: 'kotlin-kapt'
Let’s start with the Entity creation
@Entity(tableName = "MindOrksDb")
data class Chapter(
@PrimaryKey
@ColumnInfo(name = "chapterName") val chapterName: String
)
Now, we will create the DAO, database access object, it is an interface where we define all methods use to access database
@Dao
interface ChapterDao {
@Insert
fun insert(chapter: Chapter)
@Query("SELECT * FROM MindOrksDb ORDER BY chapterName ASC")
fun getAllChapter(): List<Chapter>
}
Now, we are ready to create the database. We have table info in Entity and CRUD methods in DAO interface.
@Database(entities = arrayOf(Chapter::class), version = 1)
abstract class ChapterDatabase : RoomDatabase() {
abstract fun chapterDao(): ChapterDao
companion object {
private var INSTANCE: ChapterDatabase? = null
fun getDatabase(context: Context): ChapterDatabase? {
if (INSTANCE == null) {
synchronized(ChapterDatabase::class) {
INSTANCE = Room.databaseBuilder(
context.getApplicationContext(),
ChapterDatabase::class.java, "chapter.db"
).build()
}
}
return INSTANCE
}
}
}
Let’s start with our MainActivity where user interaction has to be done, for that we need to create our main screen, activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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">
<EditText
android:id="@+id/etEnterText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:hint="Enter here"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btnSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="Save to Database"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btnDisplay"
app:layout_constraintTop_toBottomOf="@+id/etEnterText" />
<Button
android:id="@+id/btnDisplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="Display"
app:layout_constraintEnd_toStartOf="@+id/btnSave"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/etEnterText" />
<TextView
android:id="@+id/tvDatafromDb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnSave" />
</androidx.constraintlayout.widget.ConstraintLayout>
Now, In our MainActivity, let’s fill our database and get the data out of it
class MainActivity : AppCompatActivity() {
private var chapterDatabase: ChapterDatabase? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
chapterDatabase = ChapterDatabase.getDatabase(this)!!
btnSave.setOnClickListener {
if (!etEnterText.text.toString().isEmpty()) {
val chapterObj = Chapter(etEnterText.text.toString())
InsertTask(this, chapterObj).execute()
}
}
btnDisplay.setOnClickListener {
GetDataFromDb(this).execute()
}
}
private class InsertTask(var context: MainActivity, var chapter: Chapter) : AsyncTask<Void, Void, Boolean>() {
override fun doInBackground(vararg params: Void?): Boolean {
context.chapterDatabase!!.chapterDao().insert(chapter)
return true
}
override fun onPostExecute(bool: Boolean?) {
if (bool!!) {
Toast.makeText(context, "Added to Database", Toast.LENGTH_LONG).show()
}
}
}
private class GetDataFromDb(var context: MainActivity) : AsyncTask<Void, Void, List<Chapter>>() {
override fun doInBackground(vararg params: Void?): List<Chapter> {
return context.chapterDatabase!!.chapterDao().getAllChapter()
}
override fun onPostExecute(chapterList: List<Chapter>?) {
if (chapterList!!.size > 0) {
for (i in 0..chapterList.size - 1) {
context.tvDatafromDb.append(chapterList[i].chapterName)
}
}
}
}
}
Here, we had used AsyncTask which is an Android inbuilt class used to do some operation on the background thread. We will be taking this in more detail in upcoming chapters.
Let’s run this application now and add some data to the database. We had finally implemented an application using Room database library.