一、Room:SQLite 的现代化封装
Room 是 Google 在 SQLite 之上构建的 ORM 抽象层。与直接使用 SQLiteOpenHelper 相比,Room 通过编译期注解处理生成实现代码,避免了手写 SQL 字符串的繁琐和运行时反射的性能损耗。Room 的三个核心注解:**@Entity** 定义表结构,**@Dao** 定义数据访问接口,**@Database** 声明数据库。三者在编译期由 Room 的 Annotation Processor(room-compiler)生成对应的 Impl 类,底层依然使用 SupportSQLiteOpenHelper 和 SupportSQLiteDatabase 操作 SQLite。
二、核心用法与代码示例
|
Room 对协程和 Flow 的支持十分到位:DAO 方法返回 Flow<List<T>> 时,Room 会监听底层 SQLite 表的变化,任何写操作都会自动触发新数据的重新发射,实现响应式数据流。这背后依赖于 InvalidationTracker,它使用 supportSQLiteOpenHelper 的 hook 来监听数据库表的事务提交。
三、迁移(Migration)与测试
数据库版本升级时,需要定义 Migration 类并传入 addMigrations():
val MIGRATION_1_2 = object : Migration(1, 2) { |
Room 提供了内置的 MigrationTestHelper 和内存数据库支持,可以通过 Room.inMemoryDatabaseBuilder() 创建绑定到 RAM 的数据库进行测试,免去清理测试数据的麻烦。
四、Room vs 其他持久化方案
Room vs 原始 SQLite: Room 提供编译期 SQL 校验(错误拼写的 SQL 在编译期即报错)、类型安全的查询结果映射、与 LiveData/Flow/Paging 的无缝融合。Room vs Realm: Realm 基于 C++ 的底层存储引擎,在无索引扫描性能上优于 Room,但生成的 APK 体积更大且对 Kotlin 协程的支持不如 Room 原生。Room vs ObjectBox: ObjectBox 专为对象存储优化,读写速度极快,但生态和社区活跃度不及 Room。对于大多数新项目,Room 是平衡性能、易用性和 Google 官方技术栈的最佳选择。
面试常考问题
Q1: Room 如何保证 DAO 方法的线程安全?
A: Room 不会自动保证线程安全——调用方需要自行管理。但 Room 提供了一些内置保护:单例 RoomDatabase 实例具有线程安全的数据库访问(SQLiteOpenHelper 的 getWritableDatabase() 内部有读写锁);如果 DAO 方法用 suspend 修饰,Room 生成的 Impl 会将操作放到后台线程执行(通过 ArchTaskExecutor.executeOnDiskIO());对于 @Query 返回 Flow 的方法,观察在主线程进行但底层查询在后台线程。绝对不能在主线程直接调用非 suspend 的数据库操作方法(除非通过 allowMainThreadQueries() 强制允许)。AOSP 相关路径:/room/room-ktx/ 和 /room/room-runtime/src/main/java/androidx/room/。
Q2: @Insert 的 OnConflictStrategy 有哪几种策略?
A: REPLACE(冲突时删除旧行插入新行)、IGNORE(冲突时忽略新数据保留旧行)、ABORT(冲突时回滚事务,默认行为)、ROLLBACK(回滚整个事务)。选择建议:需要更新全部字段用 REPLACE,仅忽略重复用 IGNORE,严格的数据一致性场景使用默认 ABORT。
Q3: LiveData 与 Room 结合的典型使用场景?
A: Room 的 DAO 可以直接返回 LiveData<List<T>>,当数据库中的相关表发生插入、更新、删除操作时,Room 的 InvalidationTracker 会自动通知所有活跃的 LiveData 观察者重新查询。这构成了 MVVM 架构中的天然数据驱动链路:数据库变更 → Room → LiveData → UI 自动更新,全程无需手动刷新。源码路径:RoomDatabase.java 中的 createInvalidationTracker() 和 InvalidationTracker.java 中的 addObserver()。







