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 方法替代回调,避免线程管理复杂性

相关推荐
鸿蒙布道师6 小时前
鸿蒙NEXT开发Base64工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
jiet_h7 小时前
Android adb 的功能和用法
android·adb
美狐美颜sdk8 小时前
美颜SDK兼容性挑战:如何让美颜滤镜API适配iOS与安卓?
android·深度学习·ios·美颜sdk·第三方美颜sdk·视频美颜sdk
居然是阿宋8 小时前
深入理解 YUV 颜色空间:从原理到 Android 视频渲染
android·音视频
KevinWang_9 小时前
DialogFragment 不适合复用
android
古鸽100869 小时前
Audio Hal 介绍
android
小叶不焦虑10 小时前
关于 Android 系统回收站的实现
android
木西10 小时前
从0到1搭建一个RN应用从开发测试到上架全流程
android·前端·react native
小橙子207711 小时前
一条命令配置移动端(Android / iOS)自动化环境
android·ios·自动化
和煦的春风11 小时前
案例分析 | SurfaceFlinger Binder RT 被降级到CFS
android