Skip to main content
Android Development with Kotlin
CHAPTER 19 Beginner

Coroutines for Asynchronous Programming

Updated: May 16, 2026
30 min read

# Chapter 19: Local Databases with Room

<div class="toc"> <ul> <li><a href="#introduction">1. Introduction</a></li> <li><a href="#what-is-room">2. What is Room?</a></li> <li><a href="#setup">3. Adding Room to Your Project</a></li> <li><a href="#room-components">4. The Three Components of Room</a></li> <li><a href="#implementation">5. Step-by-Step Implementation</a></li> <li><a href="#using-room">6. Using the Database</a></li> <li><a href="#exercises">7. Exercises & Challenges</a></li> <li><a href="#quiz">8. Quiz</a></li> <li><a href="#interview-questions">9. Interview Questions</a></li> </ul> </div>

<h2 id="introduction">1. Introduction</h2> For storing complex, structured data locally (like a list of tasks, cached news articles, or user profiles), SharedPreferences is not enough. Android devices have a built-in relational database system called SQLite. However, using standard SQLite requires writing lots of boilerplate SQL code and manual data mapping.

<h2 id="what-is-room">2. What is Room?</h2> Room is a Jetpack Architecture Component that acts as an abstraction layer over SQLite. It maps your Kotlin objects directly to database tables (an ORM - Object Relational Mapper) and provides compile-time checks for your SQL queries, preventing app crashes from typos in SQL strings.

<h2 id="setup">3. Adding Room to Your Project</h2> Add the following dependencies to your app/build.gradle.kts file:

kotlin
12345678910111213
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("com.google.devtools.ksp") // Requires KSP plugin setup
}

dependencies {
    val room_version = "2.6.1"
    implementation("androidx.room:room-runtime:$room_version")
    ksp("androidx.room:room-compiler:$room_version")
    // Kotlin Extensions and Coroutines support for Room
    implementation("androidx.room:room-ktx:$room_version")
}

<h2 id="room-components">4. The Three Components of Room</h2> Room relies on three main annotations to generate the database code:

  1. 1. @Entity: Represents a table within the database.
  1. 2. @Dao: (Data Access Object) Contains the methods used for accessing the database (Insert, Update, Delete, Query).
  1. 3. @Database: The main access point for the underlying connection to your SQLite database.

<h2 id="implementation">5. Step-by-Step Implementation</h2>

Step 1: Create the Entity (Table)

Create a Kotlin data class and annotate it with @Entity.
kotlin
1234567891011
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "users_table")
data class User(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    val firstName: String,
    val lastName: String,
    val age: Int
)

Step 2: Create the DAO (Queries)

Create an Interface and annotate it with @Dao. Define your database operations here.
kotlin
12345678910111213141516171819
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Delete

@Dao
interface UserDao {
    @Insert
    suspend fun insertUser(user: User) // Note the 'suspend' keyword for coroutines!

    @Delete
    suspend fun deleteUser(user: User)

    @Query("SELECT * FROM users_table ORDER BY id ASC")
    suspend fun getAllUsers(): List<User>
    
    @Query("SELECT * FROM users_table WHERE age > :minAge")
    suspend fun getUsersOlderThan(minAge: Int): List<User>
}

Step 3: Create the Database

Create an abstract class extending RoomDatabase. It must be annotated with @Database.
kotlin
123456789101112131415161718192021222324252627
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 AppDatabase : RoomDatabase() {

    abstract fun userDao(): UserDao

    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getDatabase(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "app_database"
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}

*Note: The companion object implements the Singleton pattern, ensuring only one instance of the database is created, preventing memory leaks.*

<h2 id="using-room">6. Using the Database</h2> Database operations cannot be run on the Main (UI) thread, as they might take a long time and freeze the app. You must use Coroutines (or background threads).

kotlin
12345678910111213
// Inside an Activity or ViewModel
val db = AppDatabase.getDatabase(this)
val userDao = db.userDao()

// Using a Coroutine to insert data in the background
lifecycleScope.launch(Dispatchers.IO) {
    val newUser = User(firstName = "Jane", lastName = "Doe", age = 28)
    userDao.insertUser(newUser)
    
    // Read the data back
    val allUsers = userDao.getAllUsers()
    println("Database contains: $allUsers")
}

<h2 id="exercises">7. Exercises & Challenges</h2> Challenge: Create a simple "Note taking" app.

  1. 1. Create a Note entity with id, title, and content.
  1. 2. Create a NoteDao with insert, update, delete, and read-all operations.
  1. 3. Build the Database class.
  1. 4. From your Activity, insert a dummy note and print all notes to the Logcat.

<h2 id="quiz">8. Quiz</h2> Q1: What does the @Entity annotation do in Room? a) Validates the database version. b) Defines the interface for SQL queries. c) Marks a data class to be created as a database table. d) Executes database operations asynchronously. Answer: c

Q2: Which Room component handles writing the actual SQL queries (like SELECT * FROM...)? a) Repository b) Entity c) Database d) DAO Answer: d

Q3: Why must database operations be run off the main thread? a) Because Room requires Coroutines to compile. b) Because disk I/O operations can freeze the UI and cause ANR (Application Not Responding) errors. c) Because the database is locked on the main thread. d) It's just a convention, they run fine on the main thread. Answer: b

<h2 id="interview-questions">9. Interview Questions</h2> Q: What are the main advantages of using Room over standard SQLiteOpenHelper? A: Room provides compile-time verification of SQL queries, reducing runtime crashes. It abstracts away the heavy boilerplate of mapping cursors to Java/Kotlin objects via an ORM mechanism, and it integrates seamlessly with modern Android architecture components like LiveData, Flow, and Coroutines.

Q: Why do we use the Singleton pattern for the Room Database instance? A: Creating a database connection is an expensive operation. If multiple instances are created, it consumes significant memory and can lead to database locking or concurrency issues. The Singleton pattern ensures only one connection exists throughout the app's lifecycle.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·