Room database.
Apps need to persist data locally. User preferences, cached content, offline data. SQLite is the standard solution on Android, but raw SQL is tedious. Room provides a clean abstraction. Today we learn how to use Room for local persistence.
Room is a persistence library over SQLite.
It provides compile-time verification of SQL queries. Catch errors at build time, not runtime.
Room reduces boilerplate. No cursor management. No manual serialization. Just annotate classes and write queries.
Room integrates with coroutines and Flow. Suspend functions for queries. Flow for reactive updates.
Room handles the SQLite details. You work with objects and type-safe queries.
Room has three key components.
Entity is a table. A data class annotated with at Entity. Each instance is a row.
DAO is Data Access Object. An interface annotated with at Dao. Contains methods for database operations.
Database is the database holder. An abstract class annotated with at Database. Provides DAOs.
Entity defines what is stored. DAO defines how to access. Database ties them together.
An Entity is a data class representing a table.
at Entity data class User at PrimaryKey val id colon Int, val name colon String, val email colon String.
The annotation marks it as a table. The primary key uniquely identifies rows.
Column names default to property names. Customize with at ColumnInfo.
at ColumnInfo name equals "user_name" val name colon String. The column is named user_name.
Add indices for faster queries. at Entity indices equals Index "email" unique equals true.
A DAO is an interface with query methods.
at Dao interface UserDao at Query "SELECT star FROM User" suspend fun getAll colon List of User.
at Query takes a SQL string. Room verifies it at compile time.
Insert, update, delete have dedicated annotations.
at Insert suspend fun insert user colon User. at Update suspend fun update user colon User. at Delete suspend fun delete user colon User.
OnConflictStrategy handles conflicts. REPLACE replaces existing. IGNORE ignores new.
The Database class ties everything together.
at Database entities equals User version equals 1 abstract class AppDatabase colon RoomDatabase abstract fun userDao colon UserDao.
List entities in the annotation. Specify schema version.
Abstract functions expose DAOs. Room generates the implementation.
Build with Room dot databaseBuilder. Pass context, database class, and name.
Room dot databaseBuilder context AppDatabase "app_database" dot build.
DAO methods can be suspend functions.
suspend fun getAll colon List of User. The query runs off the main thread automatically.
Room handles thread switching. You just call the suspend function. Room moves work to background.
For inserts and updates too. suspend fun insert user colon User. Non-blocking database access.
No manual thread management. No AsyncTask. Just coroutines.
For reactive updates, return Flow.
fun observeAll colon Flow of List of User. No suspend needed. Returns immediately.
When database changes, Flow emits new data. Insert a user, observers get updated list.
at Query "SELECT star FROM User" fun observeAll colon Flow of List of User.
Room monitors the table. Any change triggers emission. UI stays in sync.
Combine with single source of truth pattern. UI observes database via Flow.
Schema changes require migrations.
Version 1 has User. Version 2 adds a column. Room needs instructions.
val migration_1_2 equals Migration 1 2 override fun migrate database brace database dot execSQL "ALTER TABLE User ADD COLUMN age INTEGER" close brace.
Add migration when building. dot addMigrations migration_1_2.
Without migration, data is lost. Room recreates the database.
Plan migrations early. Versioned schema evolution.
Let us summarize.
Room abstracts SQLite with compile-time verification.
Entity is a table. DAO has query methods. Database provides DAOs.
Use suspend functions for one-time queries.
Use Flow for reactive, observable queries.
Handle schema changes with migrations.
Room makes local persistence clean, safe, and reactive.