Android Room 框架数据层源码深度剖析(一)

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 框架的工作流程主要包括以下几个步骤:

  1. 定义实体类 :使用 @Entity 注解定义数据库表的结构。
  2. 定义 DAO 接口 :使用 @Dao 注解定义对数据库的操作方法。
  3. 定义数据库类 :使用 @Database 注解定义数据库类,指定实体类和 DAO 接口。
  4. 创建数据库实例 :使用 Room.databaseBuilder() 方法创建数据库实例。
  5. 获取 DAO 实例:通过数据库实例的抽象方法获取 DAO 实例。
  6. 执行数据库操作:调用 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 类中,startVersionendVersion 分别表示迁移的起始版本号和结束版本号,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 核心设计原则

  1. 编译时检查:通过注解处理器在编译期发现 SQL 错误(如字段名错误)
  2. 最小化反射:除了数据库创建,几乎不使用反射(性能关键)
  3. 线程安全:强制后台线程操作,内部使用单线程池保证顺序
  4. 渐进式优化:从基础 SQL 到协程 / LiveData,逐步封装但不隐藏 SQL

16.3 最佳实践建议

  • 批量操作必用事务 :减少磁盘同步次数(参考 beginTransaction

  • 复杂查询用 @Relation:利用编译时生成的关联查询(避免手动 Join)

  • 监控缓存命中率 :通过自定义 SQLiteOpenHelper 优化重复查询

  • 协程优先 :使用 suspend 方法替代回调,避免线程管理复杂性

相关推荐
鸿蒙布道师17 分钟前
鸿蒙NEXT开发动画案例2
android·ios·华为·harmonyos·鸿蒙系统·arkui·huawei
androidwork21 分钟前
Kotlin Android工程Mock数据方法总结
android·开发语言·kotlin
xiangxiongfly9152 小时前
Android setContentView()源码分析
android·setcontentview
人间有清欢4 小时前
Android开发补充内容
android·okhttp·rxjava·retrofit·hilt·jetpack compose
人间有清欢5 小时前
Android开发报错解决
android
每次的天空6 小时前
Android学习总结之kotlin协程面试篇
android·学习·kotlin
每次的天空8 小时前
Android学习总结之Binder篇
android·学习·binder
峥嵘life8 小时前
Android 有线网开发调试总结
android
是店小二呀9 小时前
【算法-链表】链表操作技巧:常见算法
android·c++·算法·链表
zhifanxu11 小时前
Kotlin 遍历
android·开发语言·kotlin