目录
[一、Room 的三层架构设计](#一、Room 的三层架构设计)
[1.1 接口层(开发人员直接接触的层)](#1.1 接口层(开发人员直接接触的层))
[1.2 编译时生成层(注解处理器自动生成)](#1.2 编译时生成层(注解处理器自动生成))
[1.3 运行时核心层(Room 框架的核心类)](#1.3 运行时核心层(Room 框架的核心类))
[2.1 注解处理器的启动过程](#2.1 注解处理器的启动过程)
[2.2 Database_Impl 的生成过程](#2.2 Database_Impl 的生成过程)
[2.2.1 数据库的物理构建与初始化](#2.2.1 数据库的物理构建与初始化)
[2.2.2 DAO 接口的实例化(单例模式)](#2.2.2 DAO 接口的实例化(单例模式))
[2.2.3 数据变化追踪(InvalidationTracker)](#2.2.3 数据变化追踪(InvalidationTracker))
[2.3 Dao_Impl 的生成细节](#2.3 Dao_Impl 的生成细节)
[2.3.1 搬运工:Adapter 机制 (Insert/Update/Delete)](#2.3.1 搬运工:Adapter 机制 (Insert/Update/Delete))
[2.3.2 调度员:事务管理 (Transaction)](#2.3.2 调度员:事务管理 (Transaction))
[2.3.3 翻译官:数据转换 (Cursor to Entity)](#2.3.3 翻译官:数据转换 (Cursor to Entity))
[2.3.4 魔法师:响应式查询 (ComputableLiveData)](#2.3.4 魔法师:响应式查询 (ComputableLiveData))
[3.1 RoomDatabase.Builder 的构建过程](#3.1 RoomDatabase.Builder 的构建过程)
系列入口导航:Android Jetpack 概述
Room 是 Google 推出的 Android 官方 ORM(对象关系映射)数据库解决方案,它是 Jetpack 组件库的重要组成部分。
在上一篇文章中,我们定义了Entity、Dao 和 Database 三个核心组件,轻松完成数据库操作。但是你有没有想过,这些看似简单的注解背后,Room 是如何工作的?SQLite 的原始 SQL 语句是谁帮我们生成的?数据库连接是如何管理的?事务又是如何处理的?
本文将带你深入 Room 的源码世界,从编译时注解处理到运行时执行流程,全面剖析 Room 的内部运行机制。
一、Room 的三层架构设计
在深入源码之前,我们先来理解 Room 的整体架构设计。Room 的设计遵循了经典的抽象分层原则,总共分为三层:
1.1 接口层(开发人员直接接触的层)
这一层是我们开发者日常编写的内容:
- Entity:使用 @Entity 注解的 Java 类,代表数据库中的一张表。
- Dao:使用 @Dao 注解的 Java 接口或抽象类,定义数据库操作方法。
- Database:使用 @Database 注解的抽象类,继承自 RoomDatabase,作为数据库的入口。
1.2 编译时生成层(注解处理器自动生成)
这一层是 Room 在编译时自动生成的,开发者通常看不到但可以反编译查看:
-
Database_Impl :继承自我们定义的 Database 抽象类,实现了数据库的创建、升级、连接管理等功能。
-
Dao_Impl :实现了我们定义的 Dao 接口,包含了具体的 SQL 执行逻辑。
-
Entity 相关的辅助类 :用于处理字段映射、类型转换等。
1.3 运行时核心层(Room 框架的核心类)
这一层是 Room 框架自带的,是所有 Room 应用共享的基础设施:
-
RoomDatabase:数据库的抽象基类,管理数据库连接、事务、语句池等
-
InvalidationTracker:追踪表数据的变化,用于支持 LiveData 的自动更新
-
RoomStatementPool:SQL 语句池,缓存预编译语句提升性能
-
Migration:数据库迁移的抽象类
理解了这三层架构,我们就能更好地理解 Room 的工作流程了。
二、编译时注解处理详解
Room 最核心的设计思想之一就是"编译时代码生成"。这样做的好处是:运行时不需要使用反射,避免了性能损耗;可以在编译期发现 SQL 语法错误;生成的代码是类型安全的。
2.1 注解处理器的启动过程
Room 的注解处理器入口是 RoomProcessor类,它使用了 Google 的AutoService 库来自动注册。真正的 RoomProcessor 在 2.0 版本之后就已经是 Kotlin 写的了,让我们模拟它是如何工作的:
java
// RoomProcessor 的启动和配置
@AutoService(Processor.class) // 自动注册到 javax.annotation.processing.Processor
@SupportedAnnotationTypes({ // 声明要处理的注解类型
"androidx.room.Database",
"androidx.room.Entity",
"androidx.room.Dao",
"androidx.room.Query",
"androidx.room.Insert",
"androidx.room.Update",
"androidx.room.Delete"
})
@SupportedSourceVersion(SourceVersion.RELEASE_7) // 支持的 Java 版本
public class RoomProcessor extends AbstractProcessor {
// 核心工具类,在 init 方法中初始化
private Elements elementUtils; // 用于处理 Element(类、方法、字段等的符号表示)
private Types typeUtils; // 用于类型操作(判断类型、获取父类等)
private Filer filer; // 用于创建源文件和资源文件
private Messager messager; // 用于输出编译日志和错误信息
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
// 获取各种实用工具
elementUtils = processingEnv.getElementUtils();
typeUtils = processingEnv.getTypeUtils();
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
// 打印日志表示处理器已经启动
messager.printMessage(Diagnostic.Kind.NOTE,
"RoomProcessor initialized successfully");
}
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
// 第一步:处理 Database 注解,生成 Database_Impl 类
processDatabase(roundEnv);
// 第二步:处理所有被 @Entity 注解的类
processEntities(roundEnv);
// 第三步:处理所有被 @Dao 注解的接口或抽象类
processDaos(roundEnv);
// 返回 true 表示注解已经被处理完毕,不需要交给其他处理器
return true;
}
}
| 工具类 | 在 Room 中的具体脏活累活 |
|---|---|
Elements |
用来获取类名、方法名。比如:获取 @Dao 接口里所有的函数签名。 |
Types |
最核心。 用来判断类型兼容性。比如:判断 @Query 方法的返回值是不是 List 或 LiveData。 |
Filer |
最终的大印。当 Room 拼凑好 AppDatabase_Impl.java 的字符串后,调用 filer 把它写成真正的物理文件。 |
Messager |
负责让你"红温"。如果你 SQL 写错了,你在控制台看到的 error: [Room/SqlParser] SQL error... 就是它喷出来的。 |
当你启动编译时,RoomProcessor 的核心逻辑是这样运作的:
第一步:抓取入口(@Database)
处理器首先寻找 @Database 注解。这是它的"根"。它会从 @Database(entities = {User.class}, ...) 中拿到所有的 Entity 类列表。
第二步:向下钻取(解析 Entity)
拿到 Entity 列表后,它会立刻跳转去解析这些 Entity:
- 检查字段类型。
- 寻找 @PrimaryKey。
- 验证索引(Index)是否合法。
注意: 此时它还没开始写代码,只是把这些信息存在内存里的 Entity 对象中。
第三步:横向关联(解析 Dao)
接着,它会扫描 Database 类里定义的抽象方法。比如你写了 public abstract UserDao userDao();。
- 它会跳进 UserDao 去解析每一个 @Query。
- 关键动作: 此时它会把 Dao 里的 SQL 语句拿到刚才解析好的 Entity 信息里去比对。如果你的 SQL 写的是 SELECT * FROM students,但你的 Entity 定义的是 User 表,它在这里就会直接报错中止。
第四步:整体验证与代码生成
只有当所有的 Database -> Entity -> Dao 形成了一个完整的、逻辑闭环的图形后,它才会启动 Writer(代码写入器):
- 生成 Entity 相关的代码。
- 生成 Dao 的实现类(UserDao_Impl)。
- 最后生成 Database 的实现类(AppDatabase_Impl),并在里面把之前的 Impl 实例化的过程写进去。
简单例子:我们用一个简单的电商数据库来模拟。
假设我们有 AppDatabase,包含两张表:User(用户)和 Order(订单)。Order 通过 userId 关联到 User。
java
// 1. 实体类
@Entity
public class User {
@PrimaryKey public int uid;
public String name;
}
@Entity(foreignKeys = @ForeignKey(entity = User.class,
parentColumns = "uid", childColumns = "userId"))
public class Order {
@PrimaryKey public int orderId;
public int userId; // 外键
}
// 2. DAO 接口
@Dao
public interface OrderDao {
@Query("SELECT * FROM `Order` JOIN User ON `Order`.userId = User.uid")
List<UserOrderSnapshot> getOrderDetails();
}
// 3. 数据库类
@Database(entities = {User.class, Order.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract OrderDao orderDao();
}
首先 RoomProcessor 被唤醒,它扫描到 @Database 标注了 AppDatabase。它立刻意识到:"我是为 AppDatabase 服务的,我要开始构建它的元数据图谱了。"
它不会立刻去写代码,而是先做"人口普查":
- 扫描 Entities:它看到了 User 和 Order。
- 扫描 DAOs:它看到了 OrderDao。
接着 解析 User 表:确认 uid 是主键,类型合法 。然后 解析 Order 表:
- 发现 Order 引用了 User 作为外键。
- 交叉验证:它会回头去查刚才解析好的 User 信息,确认 User 表里确实有 uid 这个字段。如果 User 里没定义 uid,这里直接报编译错误。
最后解析 OrderDao:
- 它扫描到 getOrderDetails() 里的 SQL 语句。
- SQL 语法校验:它调用内置的 SQLite 解析器。
- 表名校验:它检查 JOIN User。因为在第一步它已经把 User 加入了"全家谱",所以它知道 User 表是存在的。如果你在 @Database 的 entities 列表里漏掉了 User.class,即便你有这个类,这里也会报错:Error: There is no table named User。
代码生成(成品出炉):
- 生成 User_Order_Mapping:内部处理关联逻辑。
- 生成 OrderDao_Impl.java:把那段 JOIN 查询转换成标准的 Android Cursor 操作代码。
- 生成 AppDatabase_Impl.java:在 createOpenHelper 方法里,它会按顺序生成建表语句:先建 User,再建 Order(因为有外键依赖)。
2.2 Database_Impl 的生成过程
当我们定义一个 Database 类时,例如:
java
@Database(entities = {User.class, Order.class}, version = 2)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
public abstract OrderDao orderDao();
}
Room 的注解处理器会为我们生成 AppDatabase_Impl 类。生成的代码量很大,但核心逻辑是这样的:
java
// 这是 Room 生成的 AppDatabase_Impl 类的完整版(简化后便于理解)
public final class AppDatabase_Impl extends AppDatabase {
// Room 数据库的核心对象
private volatile SupportSQLiteOpenHelper mOpenHelper;
private volatile UserDao mUserDao;
private volatile OrderDao mOrderDao;
// 数据库配置信息
private DatabaseConfiguration mConfiguration;
// 语句池,用于缓存预编译的 SQL 语句
private RoomDatabaseStatementPool mStatementPool;
// 跟踪数据库表的变化,用于 LiveData 自动更新
private InvalidationTracker mInvalidationTracker;
@Override
protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config) {
// 保存配置信息
this.mConfiguration = config;
// 创建数据库配置构建器
Configuration.Builder builder = Configuration.builder(config.context);
// 设置数据库名称
if (config.name == null) {
// 如果数据库名称为空,使用内存数据库
builder.name(null);
} else {
builder.name(config.name);
}
// 创建数据库回调(用于监听数据库创建和打开事件)
final Callback callback = new Callback() {
@Override
public void onCreate(SupportSQLiteDatabase db) {
// 执行创建表的所有 SQL 语句
db.execSQL("CREATE TABLE IF NOT EXISTS `users` (" +
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
"`user_name` TEXT," +
"`age` INTEGER NOT NULL DEFAULT 18," +
"`created_at` INTEGER)");
db.execSQL("CREATE TABLE IF NOT EXISTS `orders` (" +
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
"`user_id` INTEGER NOT NULL," +
"`product_name` TEXT," +
"`price` REAL)");
// 创建 Room 内部使用的表,用于 InvalidationTracker
db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (" +
"id INTEGER PRIMARY KEY," +
"identity_hash TEXT)");
db.execSQL("INSERT INTO room_master_table (id, identity_hash) " +
"VALUES(42, 'room_identity_hash')");
// 创建触发器,用于监听表变化
createTriggers(db);
// 调用开发者定义的回调
if (config.callbacks != null) {
for (RoomDatabase.Callback cb : config.callbacks) {
cb.onCreate(db);
}
}
}
@Override
public void onOpen(SupportSQLiteDatabase db) {
// 数据库打开时执行
if (config.callbacks != null) {
for (RoomDatabase.Callback cb : config.callbacks) {
cb.onOpen(db);
}
}
}
};
builder.callback(callback);
// 设置数据库版本
builder.version(2);
// 设置 WAL 模式(Write-Ahead Logging)
if (config.isWriteAheadLoggingEnabled) {
builder.writeAheadLoggingEnabled(true);
}
// 创建 SQLite 辅助类
Configuration configuration = builder.build();
SupportSQLiteOpenHelper.Factory factory = new FrameworkSQLiteOpenHelperFactory();
return factory.create(configuration);
}
@Override
protected InvalidationTracker createInvalidationTracker() {
// 创建变化追踪器,监听 users 和 orders 两张表
return new InvalidationTracker(this, new HashMap<String, String>() {{
put("users", "SELECT id FROM users LIMIT 0");
put("orders", "SELECT id FROM orders LIMIT 0");
}}, new HashMap<String, Set<String>>() {{
put("users", new HashSet<String>() {{ add("users"); }});
put("orders", new HashSet<String>() {{ add("orders"); }});
}}, "users", "orders");
}
@Override
public void clearAllTables() {
// 清空所有表的数据
super.assertNotSuspendingTransaction();
SupportSQLiteDatabase db = getOpenHelper().getWritableDatabase();
db.beginTransaction();
try {
db.execSQL("DELETE FROM users");
db.execSQL("DELETE FROM orders");
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
// 通知追踪器所有表都已变化
if (mInvalidationTracker != null) {
mInvalidationTracker.refreshTrackers();
}
}
@Override
public UserDao userDao() {
// 双重检查锁,保证线程安全地获取 DAO 实例
if (mUserDao != null) {
return mUserDao;
}
synchronized (this) {
if (mUserDao == null) {
mUserDao = new UserDao_Impl(this);
}
return mUserDao;
}
}
@Override
public OrderDao orderDao() {
if (mOrderDao != null) {
return mOrderDao;
}
synchronized (this) {
if (mOrderDao == null) {
mOrderDao = new OrderDao_Impl(this);
}
return mOrderDao;
}
}
@Override
public SupportSQLiteOpenHelper getOpenHelper() {
if (mOpenHelper == null) {
synchronized (this) {
if (mOpenHelper == null) {
mOpenHelper = createOpenHelper(mConfiguration);
}
}
}
return mOpenHelper;
}
@Override
public InvalidationTracker getInvalidationTracker() {
if (mInvalidationTracker == null) {
synchronized (this) {
if (mInvalidationTracker == null) {
mInvalidationTracker = createInvalidationTracker();
}
}
}
return mInvalidationTracker;
}
// 创建触发器,用于监听表的增删改操作
private void createTriggers(SupportSQLiteDatabase db) {
// 为 users 表创建 INSERT 触发器
db.execSQL("CREATE TRIGGER IF NOT EXISTS room_trigger_users_insert " +
"AFTER INSERT ON users BEGIN " +
"INSERT OR REPLACE INTO room_table_modification_log " +
"(table_id, invalidated) VALUES (1, 1); END");
// 为 users 表创建 UPDATE 触发器
db.execSQL("CREATE TRIGGER IF NOT EXISTS room_trigger_users_update " +
"AFTER UPDATE ON users BEGIN " +
"INSERT OR REPLACE INTO room_table_modification_log " +
"(table_id, invalidated) VALUES (1, 1); END");
// 为 users 表创建 DELETE 触发器
db.execSQL("CREATE TRIGGER IF NOT EXISTS room_trigger_users_delete " +
"AFTER DELETE ON users BEGIN " +
"INSERT OR REPLACE INTO room_table_modification_log " +
"(table_id, invalidated) VALUES (1, 1); END");
// orders 表同理...
}
}
2.2.1 数据库的物理构建与初始化
这是 createOpenHelper 方法的核心工作。它负责"脏活累活":
- 建表语句 : 将你在 @Entity中定义的 Java/Kotlin 对象转化为具体的 CREATE TABLESQL 语句。
- 版本管理: 设置数据库版本,并准备处理后续的迁移(Migration)。
- 一致性校验: 创建 room_master_table并插入identity_hash。这相当于数据库的"指纹",Room 用它来确保数据库结构与代码定义完全一致,防止结构冲突。
在没有 Room 的原生 SQLite 时代,如果你修改了 Java Bean 的字段 (比如给 User 增加了一个 phone 字段),但忘记更新数据库版本号或忘记写 ALTER TABLE 语句,程序运行到查询操作时就会因为找不到列而直接 Crash。
Room 为了防止这种"低级错误"导致线上事故,引入了identity_hash(指纹) 机制。
他是如何工作的??
你可以把这个过程想象成一次"刷脸认证":
-
编译期(生成指纹): 当你点击编译代码时,Room 的注解处理器(Annotation Processor)会扫描你所有的 @Entity 类。它会把所有表名、字段名、类型、约束条件拼接成一个长字符串,然后通过 SHA-1 算法生成一个唯一的哈希值(例如:8a3b...f2c)。
-
安装/升级(保存指纹): 数据库第一次创建时 ,Room 会自动建一张"暗表" room_master_table,并把这个哈希值存进去。
-
运行期(比对指纹): 每次你通过 Room.databaseBuilder 启动 App 并打开数据库时,Room 都会做一件事:
-
计算当前代码中定义的表结构哈希值。
-
读取本地数据库文件里 room_master_table 存的哈希值。
-
对对碰: 如果两个哈希值不一致,说明你改了代码但没处理数据库迁移 。此时 Room 会直接抛出 IllegalStateException(错误信息通常是:Room cannot verify the data integrity...),而不是等到你执行 SQL 时才让它崩溃。
-
2.2.2 DAO 接口的实例化(单例模式)
代码中展示了对 userDao() 和 orderDao() 的实现,按照初始化顺序而言,先生成的应该是 Dao_Impl:
- 延迟加载: 只有在第一次调用时才会创建 DAO 实例。
- 双重检查锁(DCL): 使用 synchronized 确保在多线程环境下,每个 DAO 只有一个实例,既保证了线程安全,也优化了性能。
- 代理模式: 它返回的是 UserDao_Impl,这是 Room 自动生成的另一个类,里面包含了具体的 SQL 执行逻辑。
2.2.3 数据变化追踪(InvalidationTracker)
这是 Room 实现 LiveData 或 Flow 自动更新 UI的核心机制:
- 创建触发器(Triggers) : createTriggers方法在 SQLite 层面为每张表建立了INSERT、UPDATE、DELETE的触发器。
- 日志记录 : 一旦数据变动,触发器会向 room_table_modification_log 表中写入记录。
- 响应式更新 : InvalidationTracker 会监听这些变动并通知相关的观察者(如 LiveData),从而实现"数据库一变,UI 自动变"的效果。
数据变化的"监控探头":createTriggers
它干了什么: 在 SQLite 数据库底层埋下"地雷",一旦数据变动就触发警报。
代码中的CREATE TRIGGER是一段运行在 SQLite 内部的小程序。
- 监听动作:它紧盯 users 表。一旦你执行了 INSERT 操作(不论是从哪个 DAO 调用的),SQLite 引擎会自动触发这段逻辑。
- 记录变化:它不直接通知 UI,而是向一张 Room 自动生成的暗表 room_table_modification_log 写入一行数据:table_id=1, invalidated=1。
响应式系统的"指挥官":InvalidationTracker
它干了什么: 负责收集报警信息并通知 UI 层。
Room 在 Java 层有一个后台任务(通常在 InvalidationTracker 中),它会定期或者在特定时机去查看 room_table_modification_log这张表。
- 查账:它会执行类似 SELECT * FROM room_table_modification_log WHERE invalidated = 1 的 SQL。
- 对账:如果发现 invalidated= 1 被标记为 1 了,它就知道:"哦!users 表数据更新了!"
- 重置:查完账后,它会把 invalidated 改回 0,等待下次触发。
一旦 InvalidationTracker 确认表变了,它会查看自己的花名册:
- 谁在看这张表?:比如你写了一个 LiveData<List<User>>,Room 在生成代码时就已经把这个观察者关联到了 users 表(也就是 table_id = 1)。
- 通知重刷:Tracker 会立刻通知对应的 LiveData:"数据旧了,去重新查一遍数据库吧!"
- 最终结果:LiveData 重新执行 DAO 里的 SELECT 查询,拿到了新数据,你的 UI 就刷新了。
| 组件 | 核心职责 | 对应代码片段 |
|---|---|---|
| OpenHelper | 物理建表、版本管理、哈希校验 | createOpenHelper() |
| DAO Impl | 具体 SQL 逻辑的工厂实现 | userDao() / orderDao() |
| Triggers | 在数据库底层监控 INSERT/UPDATE/DELETE |
createTriggers() |
| Tracker | 维护表变化日志,驱动 LiveData/Flow 更新 | getInvalidationTracker() |
2.3 Dao_Impl 的生成细节
Dao_Impl 的生成是 Room 最复杂的部分之一。因为 DAO 接口中可以定义各种方法,包括不同的注解(@Insert、@Update、@Delete、@Query),每个注解的行为都不同。让我们看看 Room 是如何处理这些的:
java
// 假设我们定义了这样一个简单的 DAO 接口
@Dao
public interface UserDao {
@Insert
void insertUser(User user);
@Query("SELECT * FROM users WHERE user_name LIKE :name")
List<User> getUsersByName(String name);
@Update(onConflict = OnConflictStrategy.REPLACE)
int updateUser(User user);
@Delete
void deleteUser(User user);
@Query("SELECT * FROM users WHERE id = :userId")
LiveData<User> getUserByIdLiveData(int userId);
}
Room 会为这个接口生成 UserDao_Impl 类,完整的代码如下:
java
// UserDao_Impl 完整实现
public final class UserDao_Impl implements UserDao {
// 关联的 RoomDatabase 实例
private final RoomDatabase __db;
// 语句池,用于复用预编译的 SQL 语句
private final RoomDatabaseStatementPool __statementPool;
// 插入适配器 - 封装插入逻辑
private final InsertAdapter<User> __insertUserAdapter;
// 更新适配器
private final UpdateAdapter<User> __updateUserAdapter;
// 删除适配器
private final DeleteAdapter<User> __deleteUserAdapter;
// 查询适配器
private final QueryAdapter __getUsersByNameAdapter;
public UserDao_Impl(RoomDatabase database) {
this.__db = database;
this.__statementPool = database.getStatementPool();
// 初始化插入适配器
__insertUserAdapter = new InsertAdapter<User>(__db, "users") {
@Override
protected void bind(SupportSQLiteStatement stmt, User entity) {
// 绑定参数到 INSERT 语句
// 根据 Entity 的字段顺序来绑定
if (entity.getId() == null) {
stmt.bindNull(1);
} else {
stmt.bindLong(1, entity.getId());
}
if (entity.getUserName() == null) {
stmt.bindNull(2);
} else {
stmt.bindString(2, entity.getUserName());
}
stmt.bindLong(3, entity.getAge());
// 处理类型转换,例如 Date 转为 Long
if (entity.getCreatedAt() == null) {
stmt.bindNull(4);
} else {
stmt.bindLong(4, entity.getCreatedAt().getTime());
}
}
@Override
protected Long getPrimaryKeyFromEntity(User entity) {
// 返回实体的主键值
return entity.getId();
}
@Override
protected void setPrimaryKeyToEntity(User entity, long id) {
// 将数据库生成的 ID 回填到实体中
entity.setId(id);
}
};
// 初始化更新适配器
__updateUserAdapter = new UpdateAdapter<User>(__db, "users") {
@Override
protected void bind(SupportSQLiteStatement stmt, User entity) {
// 绑定更新语句的参数
if (entity.getUserName() == null) {
stmt.bindNull(1);
} else {
stmt.bindString(1, entity.getUserName());
}
stmt.bindLong(2, entity.getAge());
if (entity.getCreatedAt() == null) {
stmt.bindNull(3);
} else {
stmt.bindLong(3, entity.getCreatedAt().getTime());
}
if (entity.getId() == null) {
stmt.bindNull(4);
} else {
stmt.bindLong(4, entity.getId());
}
}
};
// 初始化删除适配器
__deleteUserAdapter = new DeleteAdapter<User>(__db, "users") {
@Override
protected void bind(SupportSQLiteStatement stmt, User entity) {
// 使用主键来删除
if (entity.getId() == null) {
stmt.bindNull(1);
} else {
stmt.bindLong(1, entity.getId());
}
}
};
// 初始化查询适配器
__getUsersByNameAdapter = new QueryAdapter(__db,
"SELECT * FROM users WHERE user_name LIKE ?") {
@Override
public void bindToStatement(SupportSQLiteStatement stmt,
Object[] params) {
// 绑定查询参数
String name = (String) params[0];
if (name == null) {
stmt.bindNull(1);
} else {
stmt.bindString(1, name);
}
}
@Override
public List<User> convert(Cursor cursor) {
int cursorIndex = cursor.getPosition();
try {
// 创建结果列表
List<User> result = new ArrayList<>();
while (cursor.moveToNext()) {
User user = new User();
// 根据列名获取索引并读取数据
int idIndex = cursor.getColumnIndexOrThrow("id");
user.setId(cursor.getLong(idIndex));
int userNameIndex = cursor.getColumnIndexOrThrow("user_name");
user.setUserName(cursor.getString(userNameIndex));
int ageIndex = cursor.getColumnIndexOrThrow("age");
user.setAge(cursor.getInt(ageIndex));
int createdAtIndex = cursor.getColumnIndexOrThrow("created_at");
long timestamp = cursor.getLong(createdAtIndex);
if (!cursor.isNull(createdAtIndex)) {
user.setCreatedAt(new Date(timestamp));
}
result.add(user);
}
return result;
} finally {
cursor.moveToPosition(cursorIndex);
}
}
};
}
@Override
public void insertUser(User user) {
// 确保不在主线程执行(如果启用了主线程检查)
__db.assertNotSuspendingTransaction();
// 开始事务
__db.beginTransaction();
try {
// 执行插入操作
__insertUserAdapter.insert(user);
// 标记事务成功
__db.setTransactionSuccessful();
} finally {
// 结束事务
__db.endTransaction();
}
}
@Override
public int updateUser(User user) {
__db.assertNotSuspendingTransaction();
__db.beginTransaction();
try {
int result = __updateUserAdapter.update(user);
__db.setTransactionSuccessful();
return result;
} finally {
__db.endTransaction();
}
}
@Override
public void deleteUser(User user) {
__db.assertNotSuspendingTransaction();
__db.beginTransaction();
try {
__deleteUserAdapter.delete(user);
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
}
}
@Override
public List<User> getUsersByName(String name) {
__db.assertNotSuspendingTransaction();
// 从语句池获取预编译语句
SupportSQLiteStatement stmt = __statementPool.acquire(
"SELECT * FROM users WHERE user_name LIKE ?");
try {
// 绑定参数
stmt.bindString(1, name);
// 执行查询
Cursor cursor = stmt.query();
try {
// 转换结果集
return __getUsersByNameAdapter.convert(cursor);
} finally {
cursor.close();
}
} finally {
// 归还语句到池中
__statementPool.release(stmt);
}
}
@Override
public LiveData<User> getUserByIdLiveData(int userId) {
// 创建 ComputableLiveData 来处理自动更新
final String sql = "SELECT * FROM users WHERE id = ?";
// 创建一个观察者,监听 users 表的变化
final InvalidationTracker.Observer observer =
new InvalidationTracker.Observer(new String[]{"users"}) {
@Override
public void onInvalidated(@NonNull Set<String> tables) {
// 当 users 表数据变化时,这个回调会被触发
// 我们需要通知 LiveData 重新查询
}
};
// 创建可计算的 LiveData
return new ComputableLiveData<User>(__db.getInvalidationTracker()) {
@Override
protected User compute() {
// 执行查询
SupportSQLiteStatement stmt =
__statementPool.acquire(sql);
try {
stmt.bindLong(1, userId);
Cursor cursor = stmt.query();
try {
if (cursor.moveToNext()) {
User user = new User();
user.setId(cursor.getLong(
cursor.getColumnIndexOrThrow("id")));
user.setUserName(cursor.getString(
cursor.getColumnIndexOrThrow("user_name")));
user.setAge(cursor.getInt(
cursor.getColumnIndexOrThrow("age")));
long timestamp = cursor.getLong(
cursor.getColumnIndexOrThrow("created_at"));
if (!cursor.isNull(cursor.getColumnIndexOrThrow("created_at"))) {
user.setCreatedAt(new Date(timestamp));
}
return user;
}
return null;
} finally {
cursor.close();
}
} finally {
__statementPool.release(stmt);
}
}
}.getLiveData();
}
}
如果说 Database_Impl 是地基,那么 Dao_ImpI 类就是具体的施工员。它把你定义的接口方法转化成了真正的数据库操作。
我们可以从这四个核心角色来拆解它的实现逻辑:
2.3.1 搬运工:Adapter 机制 (Insert/Update/Delete)
Room 没有使用复杂的 ORM 框架,而是通过适配器(Adapter) 手动完成 Java 对象与 SQL 参数之间的相互转换。
- 参数绑定(Binding): 看看 __insertUserAdapter。它并没有魔法,而是老老实实地调用 stmt.bindString(2, entity.getUserName())。这种硬编码的方式虽然原始,但性能极高,因为它完全避免了运行时的反射(Reflection)。
- 主键回填: 这是一个细节亮点。插入成功后,Room 会调用 setPrimaryKeyToEntity 把数据库生成的自增 ID 自动设置回你的 User 对象里。
2.3.2 调度员:事务管理 (Transaction)
观察 insertUser、updateUser 等方法,你会发现它们都被包裹在 beginTransaction 和 endTransaction之间。
__db.assertNotSuspendingTransaction() 是一道安检门,防止你在错误的线程或嵌套事务中执行非法操作。
2.3.3 翻译官:数据转换 (Cursor to Entity)
在getUsersByName的 convert 方法里,你会看到最"枯燥"的代码:从 Cursor 中根据索引取值,然后一个个 set 到 User 对象中。使用 getColumnIndexOrThrow 确保即使表结构微调,只要字段名对得上,程序就不会读错列。
2.3.4 魔法师:响应式查询 (ComputableLiveData)
这是 Room 最强大的地方:为什么你观察一个 LiveData,数据库变了它就能自动更新?
- 计算逻辑(compute): ComputableLiveData 并不是一直占着资源,只有当有人观察它时,它才会执行一次 compute() 里的 SQL 查询。
- 精准打击(Invalidation Observer): 它向 InvalidationTracker 注册了一个观察者。一旦 users 表发生了任何变动(即触发了我们之前提到的"地雷"),这个观察者就会被唤醒,并标记该 LiveData 为"失效状态",强制触发下一次 compute() 重新查询。
三、运行时核心流程解析
3.1 RoomDatabase.Builder 的构建过程
当我们调用 Room.databaseBuilder() 时,实际上是在构建一个 RoomDatabase.Builder 对象。让我们深入这个构建过程:
java
/**
* RoomDatabase 的构建器。
* * @param <T> 开发者定义的继承自 RoomDatabase 的抽象类类型。
*/
public static class Builder<T extends RoomDatabase> {
private final Class<T> mDatabaseClass;
private final String mName;
private final Context mContext;
private ArrayList<Callback> mCallbacks;
// 用于运行数据库查询的执行器(后台线程)
private Executor mQueryExecutor;
// 用于运行数据库事务的执行器(后台线程)
private Executor mTransactionExecutor;
// 数据库打开辅助类的工厂
private SupportSQLiteOpenHelper.Factory mFactory;
// 是否允许在主线程进行查询
private boolean mAllowMainThreadQueries;
// 日志模式(如 WAL)
private JournalMode mJournalMode;
// 是否启用多实例失效同步(跨进程)
private boolean mMultiInstanceInvalidation;
// 是否要求必须提供迁移逻辑(否则崩溃)
private boolean mRequireMigration;
// 是否允许在版本降级时进行破坏性删除
private boolean mAllowDestructiveMigrationOnDowngrade;
// 迁移路径容器,存储从版本 A 到版本 B 的具体 Migration 对象
private final MigrationContainer mMigrationContainer;
// 标记哪些起始版本不需要迁移,直接走破坏性重建
private Set<Integer> mMigrationsNotRequiredFrom;
// 用于校验:记录所有已添加迁移的起始和结束版本,防止逻辑冲突
private Set<Integer> mMigrationStartAndEndVersions;
Builder(@NonNull Context context, @NonNull Class<T> klass, @Nullable String name) {
mContext = context;
mDatabaseClass = klass;
mName = name;
mJournalMode = JournalMode.AUTOMATIC; // 默认为自动模式
mRequireMigration = true; // 默认要求迁移逻辑
mMigrationContainer = new MigrationContainer();
}
/**
* 设置自定义的数据库工厂。若不设置,默认使用 FrameworkSQLiteOpenHelperFactory。
*/
@NonNull
public Builder<T> openHelperFactory(@Nullable SupportSQLiteOpenHelper.Factory factory) {
mFactory = factory;
return this;
}
/**
* 添加数据库迁移路径。
* 如果版本升级时找不到对应的 Migration,Room 会清空数据库并重建。
* Room 会优先选择步长最大的路径(例如 3->5 优于 3->4->5)。
*/
@NonNull
public Builder<T> addMigrations(@NonNull Migration... migrations) {
if (mMigrationStartAndEndVersions == null) {
mMigrationStartAndEndVersions = new HashSet<>();
}
for (Migration migration : migrations) {
mMigrationStartAndEndVersions.add(migration.startVersion);
mMigrationStartAndEndVersions.add(migration.endVersion);
}
mMigrationContainer.addMigrations(migrations);
return this;
}
/**
* 禁用主线程查询检查。
* 默认开启检查以防止阻塞 UI 导致 ANR。通常仅在测试时建议关闭。
*/
@NonNull
public Builder<T> allowMainThreadQueries() {
mAllowMainThreadQueries = true;
return this;
}
/**
* 设置数据库的日志模式。内存数据库会忽略此设置。
*/
@NonNull
public Builder<T> setJournalMode(@NonNull JournalMode journalMode) {
mJournalMode = journalMode;
return this;
}
/**
* 设置普通异步查询(如 LiveData 自动刷新)所使用的执行器。
*/
@NonNull
public Builder<T> setQueryExecutor(@NonNull Executor executor) {
mQueryExecutor = executor;
return this;
}
/**
* 设置数据库事务使用的执行器。
* 为了避免死锁,如果该执行器是共享的,建议设置其为无界线程池。
*/
@NonNull
public Builder<T> setTransactionExecutor(@NonNull Executor executor) {
mTransactionExecutor = executor;
return this;
}
/**
* 启用多实例失效同步。
* 当一个数据库实例修改了表,其他进程/实例的相同数据库会收到通知,常用于多进程 App。
*/
@NonNull
public Builder<T> enableMultiInstanceInvalidation() {
mMultiInstanceInvalidation = mName != null;
return this;
}
/**
* 当找不到迁移路径时,允许 Room 破坏性地删除并重建所有表(会导致数据丢失)。
*/
@NonNull
public Builder<T> fallbackToDestructiveMigration() {
mRequireMigration = false;
mAllowDestructiveMigrationOnDowngrade = true;
return this;
}
/**
* 仅在数据库版本降级时,允许破坏性重建。
*/
@NonNull
public Builder<T> fallbackToDestructiveMigrationOnDowngrade() {
mRequireMigration = true;
mAllowDestructiveMigrationOnDowngrade = true;
return this;
}
/**
* 指定特定的起始版本,从这些版本升级时允许破坏性重建。
*/
@NonNull
public Builder<T> fallbackToDestructiveMigrationFrom(int... startVersions) {
if (mMigrationsNotRequiredFrom == null) {
mMigrationsNotRequiredFrom = new HashSet<>(startVersions.length);
}
for (int startVersion : startVersions) {
mMigrationsNotRequiredFrom.add(startVersion);
}
return this;
}
/**
* 添加数据库生命周期回调(如 onCreate, onOpen)。
*/
@NonNull
public Builder<T> addCallback(@NonNull Callback callback) {
if (mCallbacks == null) {
mCallbacks = new ArrayList<>();
}
mCallbacks.add(callback);
return this;
}
/**
* 执行最终的构建与初始化过程。
*/
@SuppressLint("RestrictedApi")
@NonNull
public T build() {
// 参数合法性校验
if (mContext == null) {
throw new IllegalArgumentException("Context 不能为空。");
}
if (mDatabaseClass == null) {
throw new IllegalArgumentException("必须提供继承自 RoomDatabase 的抽象类。");
}
// 执行器策略:若未设置,默认使用 Architecture Components 共享的 IO 线程池
if (mQueryExecutor == null && mTransactionExecutor == null) {
mQueryExecutor = mTransactionExecutor = ArchTaskExecutor.getIOThreadExecutor();
} else if (mQueryExecutor != null && mTransactionExecutor == null) {
mTransactionExecutor = mQueryExecutor;
} else if (mQueryExecutor == null && mTransactionExecutor != null) {
mQueryExecutor = mTransactionExecutor;
}
// 冲突校验:防止同一个版本既出现在 addMigrations 中,又出现在销毁重建白名单中
if (mMigrationStartAndEndVersions != null && mMigrationsNotRequiredFrom != null) {
for (Integer version : mMigrationStartAndEndVersions) {
if (mMigrationsNotRequiredFrom.contains(version)) {
throw new IllegalArgumentException(
"检测到配置冲突。版本 " + version + " 同时存在于迁移路径和破坏性重建配置中。");
}
}
}
// 默认使用 Android 系统框架的 SQLite 实现
if (mFactory == null) {
mFactory = new FrameworkSQLiteOpenHelperFactory();
}
// 汇总所有配置
DatabaseConfiguration configuration =
new DatabaseConfiguration(
mContext,
mName,
mFactory,
mMigrationContainer,
mCallbacks,
mAllowMainThreadQueries,
mJournalMode.resolve(mContext),
mQueryExecutor,
mTransactionExecutor,
mMultiInstanceInvalidation,
mRequireMigration,
mAllowDestructiveMigrationOnDowngrade,
mMigrationsNotRequiredFrom);
// 核心亮点:利用反射根据抽象类名寻找生成的实现类(如 AppDatabase_Impl)
T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
// 完成数据库初始化(准备 Tracker、OpenHelper 等)
db.init(configuration);
return db;
}
}
Room.getGeneratedImplementation是整套系统的"传送门"。它根据你传进来的抽象类名,在运行时利用反射寻找并实例化编译期生成的 _Impl 类。
- 执行器死锁风险:若自定义执行器,确保有足够的并发能力处理嵌套事务
- 多进程限制:enableMultiInstanceInvalidation 需要非内存数据库(即 mName != null)
- 反射性能:getGeneratedImplementation 只在首次 build 时调用,后续使用缓存
- 迁移优先级:Room 会优先选择步长最大的迁移路径(如 3→5 优于 3→4→5)