A Beginner's Guide to Room Database in Android (Kotlin)
Welcome to the world of local data storage on Android! This guide will teach you how
to use Room, a powerful and easy-to-use database library for Android apps. We'll go
through everything step-by-step, assuming you are a complete beginner.
What is Room?
Imagine your app needs to save some information, like a list of contacts, to-do items,
or user settings, so the data is still there even after the user closes the app. You need
a database for that.
Room is a library provided by Google that makes it much easier to create and manage
databases in your Android app. It's built on top of SQLite, which is the standard
database engine on Android, but Room simplifies everything.
Why use Room?
● Simplicity: It removes a lot of the complicated setup code you would normally
have to write.
● Safety: It checks your database queries at compile time (while you are writing the
code), which helps prevent crashes in your live app.
● Works with Modern Kotlin: It has excellent support for modern Android
development features like Coroutines and LiveData.
The Three Core Components of Room
Room has three main parts that you need to understand. Think of them as three
essential building blocks for your database.
1. The Entity (The "What")
○ What it is: An Entity is a special kind of Kotlin data class that represents a
table in your database.
○ Analogy: Think of an Excel spreadsheet. The entire spreadsheet is your
database. A single sheet (or tab) within that spreadsheet is a table. An Entity
defines the structure of that table, including its name and the columns it has.
Each row in the sheet would be a single object of your data class.
2. The DAO - Data Access Object (The "How")
○ What it is: A DAO is a Kotlin interface where you define all the database
operations you want to perform.
○ Analogy: If the Entity is the blueprint for a table, the DAO is the instruction
manual. It contains functions like insertUser, deleteUser, getAllUsers, etc. You
tell Room what you want to do, and Room automatically generates the code to
do it.
3. The Database (The "Container")
○ What it is: This is an abstract class that ties the Entities and DAOs together.
It's the main access point to your actual database.
○ Analogy: This is the main database file itself. It holds all the tables (Entities)
and provides you with the instruction manuals (DAOs) to interact with them.
Now, let's see how to build these components in code!
Step-by-Step Implementation
We will create a simple app that saves a list of users, where each user has an ID, a
name, and an email address.
Step 1: Add Room Dependencies to Your Project
First, you need to tell Android Studio that you want to use the Room library.
1. Open your project in Android Studio.
2. Find the build.gradle.kts file for your app module (it usually says (Module :app)
next to it).
3. Add the following lines inside the plugins block at the top:
plugins {
// ... other plugins
id("com.google.devtools.ksp") // Add this line
}
4. Now, add the following lines inside the dependencies block at the bottom of the
same file:
// Room Database Libraries
val room_version = "2.6.1"
implementation("androidx.room:room-runtime:$room_version")
annotationProcessor("androidx.room:room-compiler:$room_version")
// To use Kotlin Symbol Processing (KSP) for annotation processing
ksp("androidx.room:room-compiler:$room_version")
// Kotlin Coroutines for background operations
implementation("androidx.room:room-ktx:$room_version")
5. A bar will appear at the top of the file editor asking you to "Sync Now". Click it.
Android Studio will download and add the libraries to your project.
Step 2: Create the Entity
Let's define what a "User" looks like.
1. Create a new Kotlin file. Right-click on your main package name -> New -> Kotlin
Class/File.
2. Name the file User.
3. Make it a data class and add the Room annotations.
// Import the necessary Room annotations
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
// The @Entity annotation tells Room that this class represents a table in the database.
// We can give it a custom table name. If we don't, it will use the class name "User".
@Entity(tableName = "users_table")
data class User(
// The @PrimaryKey annotation marks this field as the primary key for the table.
// A primary key is a unique identifier for each row.
// `autoGenerate = true` means Room will automatically generate a unique ID for
each new user.
@PrimaryKey(autoGenerate = true)
val id: Int = 0, // It's good practice to provide a default value.
// The @ColumnInfo annotation allows us to give the column in the database a
different name.
// If we don't use it, the column name will be the same as the variable name
("name").
@ColumnInfo(name = "user_name")
val name: String,
@ColumnInfo(name = "user_email")
val email: String
)
Step 3: Create the DAO (Data Access Object)
Now, let's create the instruction manual for how to interact with our users_table.
1. Create a new Kotlin file. Right-click on your main package name -> New -> Kotlin
Class/File.
2. Name the file UserDao.
3. Make it an interface and add the DAO annotations and functions.
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
// The @Dao annotation tells Room that this is a Data Access Object.
@Dao
interface UserDao {
// The @Insert annotation is for adding new data to the table.
// `onConflict = OnConflictStrategy.IGNORE` means if we try to insert a User
// that already exists (based on its primary key), Room will just ignore the new one.
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertUser(user: User) // `suspend` makes this function runnable in a
coroutine.
// The @Query annotation is for reading data from the table.
// You write a standard SQL query inside the parentheses.
// This query selects all columns (*) from the "users_table".
@Query("SELECT * FROM users_table")
fun getAllUsers(): List<User> // This will return a list of all User objects.
}
Important Note: Database operations can take time. If you run them on the main
thread of your app, the user interface will freeze. The suspend keyword tells Kotlin
that this function should be run in the background using Coroutines, so it doesn't
block the UI.
Step 4: Create the Database Class
This is the final piece of the puzzle, where we link our User entity and UserDao.
1. Create a new Kotlin file. Right-click on your main package name -> New -> Kotlin
Class/File.
2. Name the file AppDatabase.
3. Make it an abstract class that inherits from RoomDatabase.
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
// The @Database annotation marks this class as the Room Database holder.
// `entities` is an array of all the Entity classes for this database.
// `version` is the database version. You must increment this number when you change
the database schema.
// `exportSchema = false` is fine for simple apps, it just prevents a build warning.
@Database(entities = [User::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
// This abstract function returns our DAO. Room will generate the implementation
for us.
abstract fun userDao(): UserDao
// This part is a bit of standard code to make sure we only ever have ONE instance
// of the database in our entire app. This is a best practice.
companion object {
// The @Volatile annotation means that writes to this field are immediately
// made visible to other threads.
@Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase {
// If an instance of the database already exists, return it.
// Otherwise, create a new one.
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"app_database" // The name of the actual database file.
).build()
INSTANCE = instance
// return instance
instance
}
}
}
}
Step 5: Using the Database in your App
Now that our database is fully set up, let's use it! We'll do this inside our
MainActivity.kt.
The plan is:
1. Get a reference to our database.
2. Create a new User object.
3. Use our UserDao to insert the user into the database.
4. Use our UserDao again to get all users and print them to the Logcat to see if it
worked.
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class MainActivity : AppCompatActivity() {
// Get a reference to our database instance.
// We use 'lazy' so the database is only created when we first need it.
private val database by lazy { AppDatabase.getDatabase(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// We will perform our database operations.
// It's important to do this in the background.
// We use `lifecycleScope.launch` which is a modern way to start a coroutine.
lifecycleScope.launch {
// --- 1. INSERT A USER ---
Log.d("MainActivity", "Inserting a new user...")
val newUser = User(name = "John Doe", email = "john.doe@example.com")
database.userDao().insertUser(newUser)
Log.d("MainActivity", "User inserted successfully!")
// --- 2. GET ALL USERS AND LOG THEM ---
// We use withContext(Dispatchers.IO) to ensure this read operation
// happens on a background thread.
val allUsers = withContext(Dispatchers.IO) {
database.userDao().getAllUsers()
}
Log.d("MainActivity", "--- All Users in Database ---")
if (allUsers.isEmpty()) {
Log.d("MainActivity", "No users found.")
} else {
allUsers.forEach { user ->
Log.d("MainActivity", "ID: ${user.id}, Name: ${user.name}, Email:
${user.email}")
}
}
Log.d("MainActivity", "-----------------------------")
}
}
}
To see the result:
1. Run your app on an emulator or a physical device.
2. Open the Logcat window in Android Studio (View -> Tool Windows -> Logcat).
3. In the Logcat search bar, type MainActivity.
4. You should see the log messages we wrote, including the details of the user you
just inserted! Each time you run the app, a new user will be added.
Conclusion
Congratulations! You have successfully set up and used a Room database in your
Android app.
Let's quickly recap what you learned:
● Dependencies: How to add the Room library to your project.
● Entity: How to define a database table using a data class.
● DAO: How to create an interface with functions to access your data.
● Database: How to create the main database class that brings everything
together.
● Coroutines: How to use lifecycleScope to safely perform database operations in
the background.
This is just the beginning. From here, you can explore more advanced topics like
updating and deleting data, writing more complex queries, handling database
migrations (when you change your tables), and integrating Room with LiveData or
Flow to automatically update your UI when the data changes.
Happy coding!