The Android Room Database is a powerful persistence library that simplifies database access within your Android applications. In modern Android development, the Room Persistence Library is the go-to solution for interacting with databases. It’s part of Android’s Jetpack libraries and provides a clean API for working with SQLite databases in a structured way, allowing us to write efficient, maintainable database code in Kotlin.
In this blog post, we’ll explore how to integrate Room with an Android app, from setting up dependencies to performing database operations such as inserting, reading, and deleting records.
Step 1: Setting up Room in Your Project
Add the Room dependency: Make sure to sync your project after adding these dependencies.
dependencies {
def room_version = "2.6.1"
implementation "androidx.room:room-runtime:$room_version"
// If this project uses any Kotlin source, use Kotlin Symbol Processing (KSP)
// See KSP Quickstart to add KSP to your build
ksp "androidx.room:room-compiler:$room_version"
// If this project only uses Java source, use the Java annotationProcessor
// No additional plugins are necessary
annotationProcessor "androidx.room:room-compiler:$room_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$room_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$room_version"
// To enable coroutine support
implementation "androidx.room:room-ktx:$room_version"
}
Make sure to sync your project after adding these dependencies.
Step 2: Define Entity
Entity represents a table in your database. Each field in the entity corresponds to a column in the table.
Let’s create an entity called User.
Create a Kotlin file User.kt:
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "user_table")
data class User(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val name: String,
val number: Int
)
Here, User is an entity with three fields: id, name, and number. The id is the primary key, and it auto-generates when a new record is inserted.
Step 3: Create a DAO (Data Access Object)
The DAO is an interface that contains methods for accessing the database. Here, we’ll define the basic operations like inserting, querying, and deleting User objects.
Create a Kotlin file UserDao.kt:
import androidx.lifecycle.LiveData
import androidx.room.*
@Dao
interface UserDao {
@Insert
suspend fun insert(user: User)
@Update
suspend fun update(user: User)
@Delete
suspend fun delete(user: User)
@Query("SELECT * FROM user_table")
fun getAllUsers(): LiveData<List<User>>
}
insert(), update(), and delete() are suspend functions because Room supports Kotlin coroutines for asynchronous operations.
getAllUsers() is a query method that returns a LiveData object, which automatically updates when the database changes.
Step 4: Create a Database
Now, let’s create theRoomDatabase class. This is where Room will generate the database object.
Create a Kotlin file UserDatabase.kt:
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = [User::class], version = 1, exportSchema = false)
abstract class UserDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
companion object {
@Volatile
private var INSTANCE: UserDatabase? = null
fun getDatabase(context: Context): UserDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
UserDatabase::class.java,
"user_database"
).build()
INSTANCE = instance
instance
}
}
}
We define the database version and list of entities (User in this case).
getDatabase() is a singleton method that ensures only one instance of the database is created throughout the app.
Step 5: Creating a Repository
The Repository acts as a mediator between the DAO and the UI. It abstracts access to the data and performs operations in a background thread using Kotlin coroutines.
Create a Kotlin fileUserRepository.kt:
import android.app.Application
import androidx.lifecycle.LiveData
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class UserRepository(application: Application) {
private val userDao: UserDao = UserDatabase.getDatabase(application).userDao()
val allUsers: LiveData<List<User>> = userDao.getAllUsers()
suspend fun insert(user: User) {
withContext(Dispatchers.IO) {
userDao.insert(user)
}
}
suspend fun update(user: User) {
withContext(Dispatchers.IO) {
userDao.update(user)
}
}
suspend fun delete(user: User) {
withContext(Dispatchers.IO) {
userDao.delete(user)
}
}
}
Here, we perform the database operations in a background thread using withContext(Dispatchers.IO).
Step 6: ViewModel
Now, let’s create a ViewModel to manage UI-related data and interact with the repository.
Create a Kotlin file UserViewModel.kt:
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class UserViewModel(application: Application) : AndroidViewModel(application) {
private val repository: UserRepository = UserRepository(application)
val allUsers: LiveData<List<User>> = repository.allUsers
fun insert(user: User) {
CoroutineScope(Dispatchers.Main).launch {
repository.insert(user)
}
}
fun update(user: User) {
CoroutineScope(Dispatchers.Main).launch {
repository.update(user)
}
}
fun delete(user: User) {
CoroutineScope(Dispatchers.Main).launch {
repository.delete(user)
}
}
}
The ViewModel makes it easy to interact with the repository while keeping the UI lifecycle in mind.
Step 7: Using Room in Activity
Finally, let’s use everything in an Activity. In your MainActivity.kt:
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private val userViewModel: UserViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val adapter = UserAdapter()
recyclerView.adapter = adapter
userViewModel.allUsers.observe(this, Observer { users ->
users?.let { adapter.submitList(it) }
})
// Adding a new user
buttonAddUser.setOnClickListener {
val user = User(name = "John Doe", age = 28)
userViewModel.insert(user)
}
}
}
We observe allUsers LiveData to get real-time updates from the database.
We can add a new user by clicking the button, and the data will be inserted into the Room database.