Android Room 框架数据层源码深度剖析
一、引言
在 Android 开发中,数据持久化是一个至关重要的功能。用户在应用中产生的数据,如用户信息、设置选项、缓存数据等,都需要进行有效的存储,以便在应用重启或设备重启后能够继续使用。Android 提供了多种数据持久化方案,如 SharedPreferences、SQLite 数据库等。然而,传统的 SQLite 数据库使用起来较为繁琐,需要编写大量的 SQL 语句,并且在处理数据库操作时容易出现错误。
Android Room 框架是 Google 推出的一个用于简化 SQLite 数据库操作的抽象层,它基于 SQLite 构建,提供了一套更简洁、更安全、更高效的 API 来处理数据库操作。Room 框架主要由三个主要组件构成:数据库(Database)、实体(Entity)和数据访问对象(DAO)。这些组件共同构成了 Room 框架的数据层,负责与数据库进行交互,将数据从数据库中读取出来,并将数据写入数据库。
本文将深入剖析 Android Room 框架的数据层,从源码的角度详细分析每个组件的实现原理和工作流程。通过对源码的分析,我们可以更好地理解 Room 框架的工作机制,从而更高效地使用 Room 框架进行数据持久化开发。
二、Room 框架概述
2.1 Room 框架的组件
Room 框架主要由以下三个核心组件组成:
2.1.1 实体(Entity)
实体是数据库中的表,它是一个 Java 或 Kotlin 类,使用 @Entity
注解进行标记。实体类中的每个字段对应数据库表中的一列,通过 @PrimaryKey
注解可以指定主键。例如:
java
java
import androidx.room.Entity;
import androidx.room.PrimaryKey;
// 使用 @Entity 注解标记该类为数据库实体类,对应数据库中的 "users" 表
@Entity(tableName = "users")
public class User {
// 使用 @PrimaryKey 注解指定该字段为主键,autoGenerate = true 表示主键自动生成
@PrimaryKey(autoGenerate = true)
private int id;
private String name;
private int age;
// 构造函数
public User(String name, int age) {
this.name = name;
this.age = age;
}
// Getter 和 Setter 方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
2.1.2 数据访问对象(DAO)
DAO 是一个接口或抽象类,使用 @Dao
注解进行标记。DAO 中定义了对数据库的各种操作方法,如插入、查询、更新和删除等。例如:
java
java
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import java.util.List;
// 使用 @Dao 注解标记该接口为数据访问对象
@Dao
public interface UserDao {
// 使用 @Insert 注解标记该方法为插入操作
@Insert
void insertUser(User user);
// 使用 @Query 注解标记该方法为查询操作,执行 SQL 语句查询所有用户
@Query("SELECT * FROM users")
List<User> getAllUsers();
}
2.1.3 数据库(Database)
数据库是一个抽象类,继承自 RoomDatabase
,使用 @Database
注解进行标记。数据库类中定义了所有的实体类和 DAO 接口,通过 Room.databaseBuilder()
方法可以创建数据库实例。例如:
java
java
import androidx.room.Database;
import androidx.room.RoomDatabase;
// 使用 @Database 注解标记该类为数据库类,指定实体类和数据库版本
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
// 抽象方法,用于获取 UserDao 实例
public abstract UserDao userDao();
}
2.2 Room 框架的工作流程
Room 框架的工作流程主要包括以下几个步骤:
- 定义实体类 :使用
@Entity
注解定义数据库表的结构。 - 定义 DAO 接口 :使用
@Dao
注解定义对数据库的操作方法。 - 定义数据库类 :使用
@Database
注解定义数据库类,指定实体类和 DAO 接口。 - 创建数据库实例 :使用
Room.databaseBuilder()
方法创建数据库实例。 - 获取 DAO 实例:通过数据库实例的抽象方法获取 DAO 实例。
- 执行数据库操作:调用 DAO 实例的方法执行数据库操作。
三、实体(Entity)源码分析
3.1 @Entity
注解
@Entity
注解是 Room 框架中用于标记实体类的注解,它的定义如下:
java
java
package androidx.room;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 注解的保留策略为运行时,这样在运行时可以通过反射获取注解信息
@Retention(RetentionPolicy.RUNTIME)
// 注解的目标类型为类、接口或枚举
@Target(ElementType.TYPE)
public @interface Entity {
// 数据库表的名称,默认为实体类的简单名称
String tableName() default "";
// 索引数组,用于定义数据库表的索引
Index[] indices() default {};
// 主键的列名数组
String[] primaryKeys() default {};
// 是否忽略冲突,默认为 false
boolean ignoreConflict() default false;
// 外键数组,用于定义数据库表的外键关系
ForeignKey[] foreignKeys() default {};
// 继承的主键是否自动生成,默认为 false
boolean inheritSuperIndices() default false;
}
@Entity
注解的主要作用是将一个 Java 或 Kotlin 类映射到数据库中的一个表,通过 tableName
属性可以指定表的名称,indices
属性可以定义表的索引,primaryKeys
属性可以指定主键,foreignKeys
属性可以定义外键关系等。
3.2 实体类的生成
在编译时,Room 框架会根据实体类的定义生成相应的代码。例如,对于上面定义的 User
实体类,Room 框架会生成一个 User_Impl
类,该类实现了 User
类的所有方法,并处理与数据库的交互。以下是简化后的 User_Impl
类的代码示例:
java
java
import androidx.room.EntityInsertionAdapter;
import androidx.room.RoomDatabase;
import androidx.room.RoomSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteStatement;
import java.util.List;
// 该类是 User 实体类的实现类,处理与数据库的交互
public final class User_Impl extends User {
private final RoomDatabase __db;
private final EntityInsertionAdapter<User> __insertionAdapterOfUser;
public User_Impl(RoomDatabase db) {
this.__db = db;
// 创建 User 实体的插入适配器
this.__insertionAdapterOfUser = new EntityInsertionAdapter<User>(db) {
@Override
public String createQuery() {
// 生成插入语句
return "INSERT OR ABORT INTO `users` (`id`,`name`,`age`) VALUES (nullif(?, 0),?,?)";
}
@Override
public void bind(SupportSQLiteStatement stmt, User value) {
// 绑定插入语句的参数
stmt.bindLong(1, value.getId());
stmt.bindString(2, value.getName());
stmt.bindLong(3, value.getAge());
}
};
}
public void insertUser(User user) {
__db.beginTransaction();
try {
// 执行插入操作
__insertionAdapterOfUser.insert(user);
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
}
}
public List<User> getAllUsers() {
// 生成查询语句
RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire("SELECT * FROM users", 0);
__db.assertNotSuspendingTransaction();
// 获取查询结果
List<User> _result = __db.query(_statement).getAllUsers();
_statement.release();
return _result;
}
}
在 User_Impl
类中,__insertionAdapterOfUser
是一个 EntityInsertionAdapter
实例,用于处理实体类的插入操作。createQuery()
方法生成插入语句,bind()
方法将实体类的属性值绑定到插入语句的参数上。insertUser()
方法执行插入操作,getAllUsers()
方法执行查询操作。
四、数据访问对象(DAO)源码分析
4.1 @Dao
注解
@Dao
注解是 Room 框架中用于标记数据访问对象的注解,它的定义如下:
java
java
package androidx.room;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 注解的保留策略为运行时,这样在运行时可以通过反射获取注解信息
@Retention(RetentionPolicy.RUNTIME)
// 注解的目标类型为类、接口或枚举
@Target(ElementType.TYPE)
public @interface Dao {
}
@Dao
注解的主要作用是将一个接口或抽象类标记为数据访问对象,Room 框架会根据该接口或抽象类的方法定义生成相应的实现类。
4.2 DAO 方法注解
DAO 接口中的方法需要使用特定的注解来标记不同的数据库操作,常见的注解有 @Insert
、@Query
、@Update
和 @Delete
。
4.2.1 @Insert
注解
@Insert
注解用于标记插入操作的方法,它的定义如下:
java
java
package androidx.room;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 注解的保留策略为运行时,这样在运行时可以通过反射获取注解信息
@Retention(RetentionPolicy.RUNTIME)
// 注解的目标类型为方法
@Target(ElementType.METHOD)
public @interface Insert {
// 插入冲突的处理策略,默认为 OnConflictStrategy.ABORT
int onConflict() default OnConflictStrategy.ABORT;
}
@Insert
注解的 onConflict
属性用于指定插入冲突的处理策略,常见的处理策略有 OnConflictStrategy.ABORT
(冲突时中止操作)、OnConflictStrategy.REPLACE
(冲突时替换原有数据)等。
4.2.2 @Query
注解
@Query
注解用于标记查询操作的方法,它的定义如下:
java
java
package androidx.room;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 注解的保留策略为运行时,这样在运行时可以通过反射获取注解信息
@Retention(RetentionPolicy.RUNTIME)
// 注解的目标类型为方法
@Target(ElementType.METHOD)
public @interface Query {
// SQL 查询语句
String value();
}
@Query
注解的 value
属性用于指定 SQL 查询语句,Room 框架会根据该语句执行查询操作。
4.2.3 @Update
注解
@Update
注解用于标记更新操作的方法,它的定义如下:
java
java
package androidx.room;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 注解的保留策略为运行时,这样在运行时可以通过反射获取注解信息
@Retention(RetentionPolicy.RUNTIME)
// 注解的目标类型为方法
@Target(ElementType.METHOD)
public @interface Update {
// 更新冲突的处理策略,默认为 OnConflictStrategy.ABORT
int onConflict() default OnConflictStrategy.ABORT;
}
@Update
注解的 onConflict
属性用于指定更新冲突的处理策略。
4.2.4 @Delete
注解
@Delete
注解用于标记删除操作的方法,它的定义如下:
java
java
package androidx.room;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 注解的保留策略为运行时,这样在运行时可以通过反射获取注解信息
@Retention(RetentionPolicy.RUNTIME)
// 注解的目标类型为方法
@Target(ElementType.METHOD)
public @interface Delete {
}
4.3 DAO 实现类的生成
在编译时,Room 框架会根据 DAO 接口的定义生成相应的实现类。例如,对于上面定义的 UserDao
接口,Room 框架会生成一个 UserDao_Impl
类,该类实现了 UserDao
接口的所有方法。以下是简化后的 UserDao_Impl
类的代码示例:
java
java
import androidx.room.EntityInsertionAdapter;
import androidx.room.RoomDatabase;
import androidx.room.RoomSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteStatement;
import java.util.List;
// 该类是 UserDao 接口的实现类,处理与数据库的交互
public final class UserDao_Impl implements UserDao {
private final RoomDatabase __db;
private final EntityInsertionAdapter<User> __insertionAdapterOfUser;
public UserDao_Impl(RoomDatabase db) {
this.__db = db;
// 创建 User 实体的插入适配器
this.__insertionAdapterOfUser = new EntityInsertionAdapter<User>(db) {
@Override
public String createQuery() {
// 生成插入语句
return "INSERT OR ABORT INTO `users` (`id`,`name`,`age`) VALUES (nullif(?, 0),?,?)";
}
@Override
public void bind(SupportSQLiteStatement stmt, User value) {
// 绑定插入语句的参数
stmt.bindLong(1, value.getId());
stmt.bindString(2, value.getName());
stmt.bindLong(3, value.getAge());
}
};
}
@Override
public void insertUser(User user) {
__db.beginTransaction();
try {
// 执行插入操作
__insertionAdapterOfUser.insert(user);
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
}
}
@Override
public List<User> getAllUsers() {
// 生成查询语句
RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire("SELECT * FROM users", 0);
__db.assertNotSuspendingTransaction();
// 获取查询结果
List<User> _result = __db.query(_statement).getAllUsers();
_statement.release();
return _result;
}
}
在 UserDao_Impl
类中,__insertionAdapterOfUser
是一个 EntityInsertionAdapter
实例,用于处理实体类的插入操作。insertUser()
方法执行插入操作,getAllUsers()
方法执行查询操作。
五、数据库(Database)源码分析
5.1 @Database
注解
@Database
注解是 Room 框架中用于标记数据库类的注解,它的定义如下:
java
java
package androidx.room;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 注解的保留策略为运行时,这样在运行时可以通过反射获取注解信息
@Retention(RetentionPolicy.RUNTIME)
// 注解的目标类型为类、接口或枚举
@Target(ElementType.TYPE)
public @interface Database {
// 数据库的实体类数组
Class<?>[] entities();
// 数据库的版本号
int version();
// 是否导出模式,默认为 true
boolean exportSchema() default true;
// 数据库的视图类数组
Class<?>[] views() default {};
// 数据库的迁移类数组
Migration[] migrations() default {};
}
@Database
注解的 entities
属性用于指定数据库的实体类,version
属性用于指定数据库的版本号,exportSchema
属性用于指定是否导出数据库模式,views
属性用于指定数据库的视图类,migrations
属性用于指定数据库的迁移类。
5.2 RoomDatabase
类
RoomDatabase
是 Room 框架中数据库类的基类,它是一个抽象类,定义了数据库的基本操作方法。以下是 RoomDatabase
类的部分代码示例:
java
java
package androidx.room;
import androidx.annotation.NonNull;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import java.util.concurrent.Executor;
// 数据库类的基类,定义了数据库的基本操作方法
public abstract class RoomDatabase {
private final SupportSQLiteOpenHelper mOpenHelper;
private final Executor mQueryExecutor;
protected RoomDatabase() {
// 初始化 SQLite 打开帮助类和查询执行器
mOpenHelper = createOpenHelper();
mQueryExecutor = createQueryExecutor();
}
// 抽象方法,用于创建 SQLite 打开帮助类
protected abstract SupportSQLiteOpenHelper createOpenHelper();
// 抽象方法,用于创建查询执行器
protected abstract Executor createQueryExecutor();
// 开始事务
public void beginTransaction() {
mOpenHelper.getWritableDatabase().beginTransaction();
}
// 设置事务成功
public void setTransactionSuccessful() {
mOpenHelper.getWritableDatabase().setTransactionSuccessful();
}
// 结束事务
public void endTransaction() {
mOpenHelper.getWritableDatabase().endTransaction();
}
// 执行查询操作
public SupportSQLiteDatabase query(@NonNull RoomSQLiteQuery query) {
return mOpenHelper.getReadableDatabase().query(query.getSql(), query.getBindArgs());
}
// 获取 DAO 实例
public abstract <T> T getDao(Class<T> daoClass);
}
在 RoomDatabase
类中,mOpenHelper
是一个 SupportSQLiteOpenHelper
实例,用于管理 SQLite 数据库的打开和关闭。mQueryExecutor
是一个 Executor
实例,用于执行查询操作。beginTransaction()
、setTransactionSuccessful()
和 endTransaction()
方法用于管理事务,query()
方法用于执行查询操作,getDao()
方法用于获取 DAO 实例。
5.3 数据库实例的创建
通过 Room.databaseBuilder()
方法可以创建数据库实例,以下是 Room.databaseBuilder()
方法的代码示例:
java
java
package androidx.room;
import android.content.Context;
import androidx.annotation.NonNull;
// Room 类,提供数据库创建的静态方法
public class Room {
// 数据库构建器类
public static class DatabaseBuilder<T extends RoomDatabase> {
private final Context mContext;
private final Class<T> mDatabaseClass;
private final String mName;
public DatabaseBuilder(@NonNull Context context, @NonNull Class<T> databaseClass, @NonNull String name) {
mContext = context;
mDatabaseClass = databaseClass;
mName = name;
}
// 构建数据库实例
public T build() {
// 创建 SQLite 打开帮助类
SupportSQLiteOpenHelper openHelper = createOpenHelper();
// 创建数据库实例
T database = createDatabase(openHelper);
return database;
}
// 创建 SQLite 打开帮助类
private SupportSQLiteOpenHelper createOpenHelper() {
// 创建 SQLite 打开帮助类的配置
SupportSQLiteOpenHelper.Configuration configuration = SupportSQLiteOpenHelper.Configuration.builder(mContext)
.name(mName)
.callback(new RoomDatabaseCallback())
.build();
// 创建 SQLite 打开帮助类
return new FrameworkSQLiteOpenHelperFactory().create(configuration);
}
// 创建数据库实例
private T createDatabase(SupportSQLiteOpenHelper openHelper) {
try {
// 通过反射创建数据库实例
T database = mDatabaseClass.getConstructor(SupportSQLiteOpenHelper.class).newInstance(openHelper);
return database;
} catch (Exception e) {
throw new RuntimeException("Failed to create database instance", e);
}
}
}
// 创建数据库构建器
public static <T extends RoomDatabase> DatabaseBuilder<T> databaseBuilder(@NonNull Context context, @NonNull Class<T> databaseClass, @NonNull String name) {
return new DatabaseBuilder<>(context, databaseClass, name);
}
}
在 Room.databaseBuilder()
方法中,首先创建一个 DatabaseBuilder
实例,然后调用 build()
方法构建数据库实例。build()
方法中,首先调用 createOpenHelper()
方法创建 SupportSQLiteOpenHelper
实例,然后调用 createDatabase()
方法通过反射创建数据库实例。
六、数据库迁移(Migration)源码分析
6.1 Migration
类
Migration
类是 Room 框架中用于处理数据库迁移的类,它是一个抽象类,定义了数据库迁移的基本方法。以下是 Migration
类的代码示例:
java
java
package androidx.room;
import androidx.sqlite.db.SupportSQLiteDatabase;
// 数据库迁移类,定义了数据库迁移的基本方法
public abstract class Migration {
private final int startVersion;
private final int endVersion;
public Migration(int startVersion, int endVersion) {
this.startVersion = startVersion;
this.endVersion = endVersion;
}
// 获取迁移的起始版本号
public int getStartVersion() {
return startVersion;
}
// 获取迁移的结束版本号
public int getEndVersion() {
return endVersion;
}
// 抽象方法,用于执行数据库迁移操作
public abstract void migrate(@NonNull SupportSQLiteDatabase database);
}
在 Migration
类中,startVersion
和 endVersion
分别表示迁移的起始版本号和结束版本号,migrate()
方法是一个抽象方法,用于执行数据库迁移操作。
6.2 数据库迁移的执行
在 RoomDatabase
类的 createOpenHelper()
方法中,会根据 @Database
注解的 migrations
属性创建 RoomOpenHelper
实例,RoomOpenHelper
类继承自 SupportSQLiteOpenHelper.Callback
,用于处理数据库的打开和迁移操作。以下是 RoomOpenHelper
类的部分代码示例:
java
java
package androidx.room;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import java.util.List;
// Room 打开帮助类,处理数据库的打开和迁移操作
class RoomOpenHelper extends SupportSQLiteOpenHelper.Callback {
private final List<Migration> mMigrations;
private final int mVersion;
public RoomOpenHelper(List<Migration> migrations, int version) {
mMigrations = migrations;
mVersion = version;
}
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
// 创建数据库表
createAllTables(db);
}
@Override
public void onUpgrade(@NonNull SupportSQLiteDatabase db, int oldVersion, int newVersion) {
// 执行数据库迁移操作
if (mMigrations != null) {
for (Migration migration : mMigrations) {
if (migration.getStartVersion() == oldVersion && migration.getEndVersion() == newVersion) {
migration.migrate(db);
break;
}
}
}
}
// 创建所有数据库表
private void createAllTables(@NonNull SupportSQLiteDatabase db) {
// 执行创建表的 SQL 语句
db.execSQL("CREATE TABLE IF NOT EXISTS `users` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `age` INTEGER)");
}
}
在 RoomOpenHelper
类中,onCreate()
方法用于创建数据库表,onUpgrade()
方法用于执行数据库迁移操作。在 onUpgrade()
方法中,会遍历 mMigrations
列表,找到匹配的迁移类并执行 migrate()
方法。
七、线程管理源码分析
7.1 查询执行器(Executor)
在 RoomDatabase
类中,mQueryExecutor
是一个 Executor
实例,用于执行查询操作。默认情况下,Room 框架使用 Executors.newSingleThreadExecutor()
创建一个单线程执行器,确保所有的数据库操作在同一个线程中执行,避免多线程并发访问数据库导致的问题。以下是 RoomDatabase
类中创建查询执行器的代码示例:
java
java
package androidx.room;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
// 数据库类的基类,定义了数据库的基本操作方法
public abstract class RoomDatabase {
private final Executor mQueryExecutor;
protected RoomDatabase() {
// 创建查询执行器
mQueryExecutor = createQueryExecutor();
}
// 创建查询执行器
protected Executor createQueryExecutor() {
return Executors.newSingleThreadExecutor();
}
// 执行查询操作
public void query(@NonNull RoomSQLiteQuery query, @NonNull QueryCallback callback) {
mQueryExecutor.execute(() -> {
try {
// 执行查询操作
SupportSQLiteDatabase database = getOpenHelper().getReadableDatabase();
7.2 异步操作封装
Room 通过 @WorkerThread
注解强制要求数据库操作在后台线程执行,其实现依赖 QueryExecutor
:
java
java
// RoomDatabase.java
public abstract class RoomDatabase {
// 默认使用单线程执行器(避免并发问题)
private final Executor mQueryExecutor = Executors.newSingleThreadExecutor();
// 同步查询(危险,会阻塞主线程)
@Deprecated
public <T> T query(@NonNull RoomSQLiteQuery query, @NonNull RowMapper<T> rowMapper) {
checkNotMainThread(); // 核心检查!
return query(query, rowMapper, NO_SOFT_MEMORY_CACHE);
}
// 异步查询封装
public <T> LiveData<T> getRxJavaLiveData(@NonNull RoomSQLiteQuery query, @NonNull RowMapper<T> rowMapper) {
final MutableLiveData<T> liveData = new MutableLiveData<>();
mQueryExecutor.execute(() -> {
T result = query(query, rowMapper);
liveData.postValue(result); // 自动切换到主线程
});
return liveData;
}
// 线程检查核心逻辑
private void checkNotMainThread() {
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
throw new IllegalStateException(
"Cannot access database on the main thread since it may potentially lock the UI for "
+ "a long time.");
}
}
}
7.3 协程支持源码
Room 2.2+ 引入协程支持,核心类 SuspendSupport
:
java
java
// SuspendSupport.java(内部类)
static final class SuspendSupport {
// 协程调度器(默认使用 IO 调度器)
static final CoroutineDispatcher DEFAULT_DISPATCHER = Dispatchers.IO;
// 挂起函数封装
static <T> T executeSuspending(@NonNull RoomDatabase db, @NonNull Callable<T> callable) {
if (Thread.currentThread() == db.mQueryExecutor) { // 避免嵌套调度
return callable.call();
}
return withContext(DEFAULT_DISPATCHER) { callable.call() }; // 自动切换线程
}
}
// 在 DAO 协程方法中调用
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getUser(id: Long): User
}
// 生成的实现类
public final class UserDao_Impl implements UserDao {
@Override
public User getUser(long id) {
return SuspendSupport.executeSuspending(__db, () -> {
// 这里执行实际查询
return __db.query(/* ... */).getUser();
});
}
}
八、事务管理源码解析
8.1 显式事务实现
java
java
// RoomDatabase.java
public void beginTransaction() {
mOpenHelper.getWritableDatabase().beginTransaction();
}
public void setTransactionSuccessful() {
mOpenHelper.getWritableDatabase().setTransactionSuccessful();
}
public void endTransaction() {
mOpenHelper.getWritableDatabase().endTransaction();
}
// 使用示例
db.beginTransaction();
try {
dao.insert(user1);
dao.insert(user2);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
8.2 自动事务(@Transaction 注解)
java
java
// TransactionInterceptor.java(编译时生成)
public class UserDao_TransactionInterceptor implements UserDao {
private final UserDao delegate;
private final RoomDatabase db;
public UserDao_TransactionInterceptor(UserDao delegate, RoomDatabase db) {
this.delegate = delegate;
this.db = db;
}
@Override
@Transaction
public void insertUsers(User... users) {
db.beginTransaction();
try {
for (User user : users) {
delegate.insert(user);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
}
// DAO 声明
@Dao
interface UserDao {
@Insert
void insert(User user);
@Transaction
void insertUsers(User... users); // 自动包装事务
}
九、类型转换器(TypeConverter)源码
9.1 注解处理器实现
java
java
// TypeConverterProcessor.java(Room 编译器部分)
public class TypeConverterProcessor {
public Element process(Element element) {
// 收集 @TypeConverter 方法
for (ExecutableElement method : element.getEnclosedElements()) {
if (method.getAnnotation(TypeConverter.class) != null) {
// 生成类型转换桥接类
generateTypeConverterClass(method);
}
}
}
private void generateTypeConverterClass(ExecutableElement method) {
// 生成转换逻辑
String convertCode = String.format(
"public %s to%s(%s value) { return %s; }",
method.getReturnType(),
method.getSimpleName(),
method.getParameters().get(0).asType(),
method.getBody().toString()
);
}
}
9.2 运行时调用
java
java
// TypeConvertersHolder.java(生成代码)
public class TypeConvertersHolder {
public static Date fromTimestamp(Long value) {
return value == null ? null : new Date(value);
}
public static Long toTimestamp(Date value) {
return value == null ? null : value.getTime();
}
}
// 字段绑定逻辑(EntityInsertionAdapter)
@Override
public void bind(SupportSQLiteStatement stmt, User value) {
stmt.bindLong(1, TypeConvertersHolder.toTimestamp(value.getBirthday()));
}
十、预编译语句(Prepared Statement)优化
10.1 语句缓存机制
java
java
// FrameworkSQLiteOpenHelper.java
public class FrameworkSQLiteOpenHelper extends SupportSQLiteOpenHelper {
private final SparseArray<SupportSQLiteStatement> mStmtCache = new SparseArray<>();
@NonNull
@Override
public SupportSQLiteStatement compileStatement(@NonNull String sql) {
int key = sql.hashCode();
SupportSQLiteStatement stmt = mStmtCache.get(key);
if (stmt != null) return stmt;
stmt = super.compileStatement(sql);
mStmtCache.put(key, stmt);
return stmt;
}
}
// DAO 方法生成代码
public void insert(User user) {
final String sql = "INSERT INTO users(name, age) VALUES (?, ?)";
SupportSQLiteStatement stmt = __db.compileStatement(sql); // 从缓存获取
stmt.bindString(1, user.getName());
stmt.bindLong(2, user.getAge());
stmt.executeInsert();
}
10.2 批量操作优化
java
java
// 生成的批量插入代码
public void insertAll(List<User> users) {
__db.beginTransaction();
try {
final String sql = "INSERT INTO users(name, age) VALUES (?, ?)";
SupportSQLiteStatement stmt = __db.compileStatement(sql);
for (User user : users) {
stmt.bindString(1, user.getName());
stmt.bindLong(2, user.getAge());
stmt.executeInsert();
stmt.clearBindings(); // 重用语句
}
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
}
}
十一、LiveData 集成源码
11.1 观察器封装
java
java
// LiveDataRoomCallback.java
public class LiveDataRoomCallback implements RoomSQLiteQuery.Callback {
private final MutableLiveData<List<User>> liveData;
public LiveDataRoomCallback(MutableLiveData<List<User>> liveData) {
this.liveData = liveData;
}
@Override
public void onQueryResult(QueryResult result) {
liveData.postValue(mapToUsers(result)); // 主线程更新
}
}
// DAO 方法生成
@Query("SELECT * FROM users")
LiveData<List<User>> observeUsers();
// 生成代码
public LiveData<List<User>> observeUsers() {
final RoomSQLiteQuery query = RoomSQLiteQuery.acquire("SELECT * FROM users", 0);
final MutableLiveData<List<User>> liveData = new MutableLiveData<>();
__db.getInvalidationTracker().addWeakObserver(query, new LiveDataRoomCallback(liveData));
return liveData;
}
11.2 失效追踪(InvalidationTracker)
java
java
// InvalidationTracker.java
public class InvalidationTracker {
private final Map<RoomSQLiteQuery, Set<InvalidationObserver>> mObservers = new HashMap<>();
public void addWeakObserver(RoomSQLiteQuery query, InvalidationObserver observer) {
mObservers.computeIfAbsent(query, k -> new HashSet<>()).add(observer);
}
void notifyInvalidated(RoomSQLiteQuery query) {
mObservers.getOrDefault(query, Collections.emptySet()).forEach(observer -> {
observer.onInvalidated(); // 触发 LiveData 更新
});
}
}
// 插入操作触发失效
@Insert
public void insert(User user) {
// 执行插入...
__db.getInvalidationTracker().notifyInvalidated(/* users 表相关查询 */);
}
十二、复杂查询优化:关联查询
12.1 嵌套对象查询
java
java
// 实体类
@Entity(tableName = "users")
public class User {
@PrimaryKey public long id;
public String name;
}
@Entity(tableName = "emails", foreignKeys = @ForeignKey(entity = User.class, parentColumns = "id", childColumns = "userId"))
public class Email {
@PrimaryKey public long id;
public long userId;
public String address;
}
// 关联对象
public class UserWithEmails {
@Embedded public User user;
@Relation(parentColumn = "id", entityColumn = "userId")
public List<Email> emails;
}
// DAO 方法
@Query("SELECT * FROM users")
LiveData<List<UserWithEmails>> getUserWithEmails();
12.2 编译时生成的关联查询
java
java
// UserWithEmailsMapper.java(生成代码)
public class UserWithEmailsMapper {
public UserWithEmails map(Cursor cursor) {
User user = new User();
user.id = cursor.getLong(0);
user.name = cursor.getString(1);
List<Email> emails = new ArrayList<>();
// 关联查询子游标(通过 SQLite 的 WITHOUT ROWID 优化)
try (Cursor emailCursor = __db.query("SELECT * FROM emails WHERE userId = ?", user.id)) {
while (emailCursor.moveToNext()) {
Email email = new Email();
email.id = emailCursor.getLong(0);
email.userId = emailCursor.getLong(1);
email.address = emailCursor.getString(2);
emails.add(email);
}
}
return new UserWithEmails(user, emails);
}
}
十三、性能优化深度解析
13.1 批量操作对比(源码测试)
java
java
// 无事务批量插入(1000 条)
long start = System.currentTimeMillis();
for (int i=0; i<1000; i++) {
dao.insert(new User("User" + i, 20));
}
Log.d("PERF", "No Transaction: " + (System.currentTimeMillis() - start)); // ~120ms
// 有事务批量插入
db.beginTransaction();
try {
for (int i=0; i<1000; i++) {
dao.insert(new User("User" + i, 20));
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
Log.d("PERF", "With Transaction: " + (System.currentTimeMillis() - start)); // ~15ms
13.2 预编译语句复用率
java
java
// FrameworkSQLiteOpenHelper 统计
public class DebugSQLiteOpenHelper extends FrameworkSQLiteOpenHelper {
private int mStmtCacheHit = 0;
private int mStmtCacheMiss = 0;
@NonNull
@Override
public SupportSQLiteStatement compileStatement(@NonNull String sql) {
if (mStmtCache.indexOfKey(sql.hashCode()) >= 0) {
mStmtCacheHit++;
} else {
mStmtCacheMiss++;
}
return super.compileStatement(sql);
}
public void printCacheStats() {
Log.d("ROOM", "Stmt Cache Hit Rate: " + (mStmtCacheHit * 100f) / (mStmtCacheHit + mStmtCacheMiss));
}
}
十四、边界情况处理源码
14.1 空表查询处理
java
java
// RoomSQLiteQuery.java
public List<User> getAllUsers() {
RoomSQLiteQuery query = RoomSQLiteQuery.acquire("SELECT * FROM users", 0);
try (Cursor cursor = __db.query(query)) {
if (!cursor.moveToFirst()) { // 空表处理
return Collections.emptyList();
}
List<User> users = new ArrayList<>(cursor.getCount());
do {
users.add(mapCursorToUser(cursor));
} while (cursor.moveToNext());
return users;
}
}
14.2 外键约束处理
java
java
// 实体定义
@Entity(foreignKeys = @ForeignKey(
entity = User.class,
parentColumns = "id",
childColumns = "userId",
onDelete = ForeignKey.CASCADE
))
public class Email {
// ...
}
// 生成的删除逻辑
@Query("DELETE FROM users WHERE id = :id")
int deleteUser(long id);
// 实际执行 SQL(自动级联删除)
DELETE FROM users WHERE id = ?;
DELETE FROM emails WHERE userId = ?; // 由外键约束触发
十五、内存泄漏防范
15.1 LiveData 弱引用持有
java
java
// LiveDataRoomCallback.java
public class LiveDataRoomCallback implements InvalidationObserver {
private final WeakReference<MutableLiveData<List<User>>> mLiveDataRef;
public LiveDataRoomCallback(MutableLiveData<List<User>> liveData) {
mLiveDataRef = new WeakReference<>(liveData);
}
@Override
public void onInvalidated() {
MutableLiveData<List<User>> liveData = mLiveDataRef.get();
if (liveData != null) {
liveData.postValue(dao.getAllUsers()); // 避免内存泄漏
}
}
}
15.2 数据库连接管理
java
java
// FrameworkSQLiteOpenHelper.java
@Override
public void close() {
super.close();
mStmtCache.clear(); // 释放预编译语句
mOpenHelper.close(); // 关闭 SQLite 连接
}
// Activity 生命周期绑定
public class MainActivity extends AppCompatActivity {
private AppDatabase db;
@Override
protected void onDestroy() {
super.onDestroy();
db.close(); // 避免连接泄漏
}
}
十六、总结:Room 数据层设计哲学
16.1 源码结构总览
plaintext
java
room/
├── annotations/ # 注解定义(@Entity, @Dao 等)
├── compiler/ # 编译时处理器(生成代码)
├── runtime/ # 运行时核心(RoomDatabase, DAO 实现)
│ ├── EntityInsertionAdapter.java # 实体插入优化
│ ├── RoomSQLiteQuery.java # 查询语句封装
│ ├── InvalidationTracker.java # 数据变更追踪
│ └── SuspendSupport.java # 协程支持
├── testing/ # 测试工具
└── ktx/ # Kotlin 扩展(协程、Flow 支持)
16.2 核心设计原则
- 编译时检查:通过注解处理器在编译期发现 SQL 错误(如字段名错误)
- 最小化反射:除了数据库创建,几乎不使用反射(性能关键)
- 线程安全:强制后台线程操作,内部使用单线程池保证顺序
- 渐进式优化:从基础 SQL 到协程 / LiveData,逐步封装但不隐藏 SQL
16.3 最佳实践建议
-
批量操作必用事务 :减少磁盘同步次数(参考
beginTransaction
) -
复杂查询用
@Relation
:利用编译时生成的关联查询(避免手动 Join) -
监控缓存命中率 :通过自定义
SQLiteOpenHelper
优化重复查询 -
协程优先 :使用
suspend
方法替代回调,避免线程管理复杂性