一、引言
在 Android 开发中,数据持久化是一个常见的需求。Android Room 框架作为 Android Jetpack 组件的一部分,为开发者提供了一个抽象层,使得在 SQLite 数据库上进行数据操作变得更加简单和高效。Room 框架包含多个模块,其中公共模块是其他模块的基础,它提供了一些通用的工具类、注解和接口,为整个框架的运行提供了支撑。本文将深入剖析 Android Room 框架的公共模块,从源码级别详细分析其实现原理和使用方法。
二、公共模块概述
2.1 公共模块的作用
公共模块是 Room 框架的基础,它定义了一些通用的接口、注解和工具类,供其他模块使用。这些接口和注解为 Room 框架的各个组件提供了统一的标准和规范,使得不同模块之间能够协同工作。例如,公共模块中定义了数据库实体类的注解,以及 DAO 接口的方法注解,这些注解在编译时会被处理,生成相应的代码。
2.2 公共模块的主要组件
公共模块主要包含以下几个方面的组件:
- 注解:如
@Entity
、@Dao
、@Query
等,用于定义数据库实体类、数据访问对象和数据库查询语句。 - 接口:如
RoomDatabase
、SupportSQLiteOpenHelper
等,定义了数据库操作的基本接口。 - 工具类:如
Room
类,提供了创建数据库实例的静态方法。
三、注解分析
3.1 @Entity
注解
@Entity
注解用于定义数据库中的实体类,每个实体类对应数据库中的一张表。
java
import androidx.room.Entity;
import androidx.room.PrimaryKey;
// 使用 @Entity 注解将该类标记为数据库实体类
@Entity(tableName = "users")
public class User {
// 使用 @PrimaryKey 注解将 id 字段标记为主键
@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;
}
}
3.1.1 源码分析
@Entity
注解的定义如下:
java
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 "";
// 主键字段名数组
String[] primaryKeys() default {};
// 是否忽略冲突
boolean ignoreConflicts() default false;
// 索引数组
Index[] indices() default {};
// 外键数组
ForeignKey[] foreignKeys() default {};
}
在编译时,Room 编译器会处理 @Entity
注解,根据注解中的信息生成相应的 SQL 语句来创建数据库表。例如,对于上述 User
类,会生成如下 SQL 语句:
sql
CREATE TABLE IF NOT EXISTS `users` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `age` INTEGER)
3.2 @Dao
注解
@Dao
注解用于定义数据访问对象(DAO)接口,DAO 接口中定义了对数据库的各种操作方法。
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 insert(User user);
// 使用 @Query 注解标记查询方法
@Query("SELECT * FROM users")
List<User> getAllUsers();
}
3.2.1 源码分析
@Dao
注解的定义如下:
java
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 {
}
在编译时,Room 编译器会根据 @Dao
接口中的方法注解生成相应的实现类。例如,对于上述 UserDao
接口,会生成一个实现类,其中 insert
方法会将 User
对象插入到数据库中,getAllUsers
方法会从数据库中查询所有用户信息。
3.3 @Query
注解
@Query
注解用于定义数据库查询语句。
java
import androidx.room.Dao;
import androidx.room.Query;
import java.util.List;
// 使用 @Dao 注解将该接口标记为数据访问对象接口
@Dao
public interface UserDao {
// 使用 @Query 注解标记查询方法,查询年龄大于指定值的用户
@Query("SELECT * FROM users WHERE age > :age")
List<User> getUsersByAge(int age);
}
3.3.1 源码分析
@Query
注解的定义如下:
java
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();
}
在编译时,Room 编译器会解析 @Query
注解中的 SQL 语句,并根据方法的参数和返回值类型生成相应的查询代码。例如,对于上述 getUsersByAge
方法,会生成如下代码:
java
public List<User> getUsersByAge(int age) {
String sql = "SELECT * FROM users WHERE age > ?";
// 执行 SQL 查询
Cursor cursor = db.rawQuery(sql, new String[]{String.valueOf(age)});
try {
List<User> users = new ArrayList<>();
while (cursor.moveToNext()) {
User user = new User();
user.setId(cursor.getInt(cursor.getColumnIndex("id")));
user.setName(cursor.getString(cursor.getColumnIndex("name")));
user.setAge(cursor.getInt(cursor.getColumnIndex("age")));
users.add(user);
}
return users;
} finally {
cursor.close();
}
}
四、接口分析
4.1 RoomDatabase
接口
RoomDatabase
是 Room 框架的核心接口,它定义了数据库操作的基本方法。
java
import androidx.room.RoomDatabase;
import androidx.room.Database;
import androidx.room.Room;
import android.content.Context;
// 使用 @Database 注解定义数据库类
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
private static AppDatabase INSTANCE;
// 获取数据库实例的静态方法
public static AppDatabase getInstance(Context context) {
if (INSTANCE == null) {
synchronized (AppDatabase.class) {
if (INSTANCE == null) {
// 使用 Room.databaseBuilder 方法创建数据库实例
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, "app-database")
.build();
}
}
}
return INSTANCE;
}
}
4.1.1 源码分析
RoomDatabase
接口的定义如下:
java
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import java.util.List;
import java.util.concurrent.Executor;
// 抽象类,定义了数据库操作的基本方法
public abstract class RoomDatabase {
// 获取数据库辅助类
public abstract SupportSQLiteOpenHelper getOpenHelper();
// 获取数据库查询执行器
public abstract Executor getQueryExecutor();
// 数据库回调列表
private final List<Callback> mCallbacks = new ArrayList<>();
// 数据库是否已经打开
private boolean mIsOpen;
// 注册数据库回调
public void registerCallback(Callback callback) {
mCallbacks.add(callback);
}
// 注销数据库回调
public void unregisterCallback(Callback callback) {
mCallbacks.remove(callback);
}
// 数据库回调接口
public static abstract class Callback {
// 数据库创建时调用
public void onCreate(SupportSQLiteDatabase db) {}
// 数据库打开时调用
public void onOpen(SupportSQLiteDatabase db) {}
// 数据库升级时调用
public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {}
// 数据库降级时调用
public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {}
}
}
RoomDatabase
是一个抽象类,具体的实现由 Room 编译器生成。在上述 AppDatabase
类中,通过 @Database
注解定义了数据库类,entities
属性指定了数据库中包含的实体类,version
属性指定了数据库的版本号。getInstance
方法使用单例模式获取数据库实例,通过 Room.databaseBuilder
方法创建数据库实例。
4.2 SupportSQLiteOpenHelper
接口
SupportSQLiteOpenHelper
接口定义了数据库辅助类的基本方法,用于管理数据库的创建和版本升级。
java
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import android.content.Context;
// 实现 SupportSQLiteOpenHelper 接口的数据库辅助类
public class AppDatabaseHelper implements SupportSQLiteOpenHelper {
private final SupportSQLiteOpenHelper mDelegate;
public AppDatabaseHelper(Context context) {
// 创建 SQLiteOpenHelper 的委托对象
mDelegate = new FrameworkSQLiteOpenHelperFactory().create(new Configuration(
context,
"app-database",
null,
new SupportSQLiteOpenHelper.Callback(1) {
@Override
public void onCreate(SupportSQLiteDatabase db) {
// 创建数据库表
db.execSQL("CREATE TABLE IF NOT EXISTS `users` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `age` INTEGER)");
}
@Override
public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
// 数据库升级逻辑
if (oldVersion < 2) {
// 执行升级操作
db.execSQL("ALTER TABLE users ADD COLUMN email TEXT");
}
}
}
));
}
@Override
public String getDatabaseName() {
return mDelegate.getDatabaseName();
}
@Override
public void setWriteAheadLoggingEnabled(boolean enabled) {
mDelegate.setWriteAheadLoggingEnabled(enabled);
}
@Override
public SupportSQLiteDatabase getWritableDatabase() {
return mDelegate.getWritableDatabase();
}
@Override
public SupportSQLiteDatabase getReadableDatabase() {
return mDelegate.getReadableDatabase();
}
@Override
public void close() {
mDelegate.close();
}
}
4.2.1 源码分析
SupportSQLiteOpenHelper
接口的定义如下:
java
import androidx.sqlite.db.SupportSQLiteDatabase;
// 定义数据库辅助类的基本方法
public interface SupportSQLiteOpenHelper {
// 获取数据库名称
String getDatabaseName();
// 设置是否启用预写日志
void setWriteAheadLoggingEnabled(boolean enabled);
// 获取可写数据库
SupportSQLiteDatabase getWritableDatabase();
// 获取可读数据库
SupportSQLiteDatabase getReadableDatabase();
// 关闭数据库
void close();
// 数据库回调接口
abstract class Callback {
// 数据库版本号
public final int version;
public Callback(int version) {
this.version = version;
}
// 数据库创建时调用
public abstract void onCreate(SupportSQLiteDatabase db);
// 数据库升级时调用
public abstract void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion);
// 数据库降级时调用
public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {}
// 数据库打开时调用
public void onOpen(SupportSQLiteDatabase db) {}
}
// 数据库配置类
class Configuration {
public final Context context;
public final String name;
public final Factory factory;
public final Callback callback;
public final boolean allowMainThreadQueries;
public final boolean journalMode;
public Configuration(Context context, String name, Factory factory, Callback callback,
boolean allowMainThreadQueries, boolean journalMode) {
this.context = context;
this.name = name;
this.factory = factory;
this.callback = callback;
this.allowMainThreadQueries = allowMainThreadQueries;
this.journalMode = journalMode;
}
}
// 数据库辅助类工厂接口
interface Factory {
SupportSQLiteOpenHelper create(Configuration configuration);
}
}
SupportSQLiteOpenHelper
接口定义了数据库辅助类的基本方法,如获取数据库名称、获取可写 / 可读数据库、关闭数据库等。Callback
接口定义了数据库创建、升级、降级和打开时的回调方法。Configuration
类用于配置数据库的相关信息,Factory
接口用于创建数据库辅助类实例。在上述 AppDatabaseHelper
类中,实现了 SupportSQLiteOpenHelper
接口,并通过 FrameworkSQLiteOpenHelperFactory
创建了一个数据库辅助类的委托对象。
五、工具类分析
5.1 Room
类
Room
类是一个工具类,提供了创建数据库实例的静态方法。
java
import androidx.room.Room;
import android.content.Context;
// 使用 Room 类创建数据库实例
public class DatabaseManager {
private static AppDatabase appDatabase;
public static AppDatabase getAppDatabase(Context context) {
if (appDatabase == null) {
// 使用 Room.databaseBuilder 方法创建数据库实例
appDatabase = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, "app-database")
.build();
}
return appDatabase;
}
}
5.1.1 源码分析
Room
类的定义如下:
java
import androidx.room.RoomDatabase;
import androidx.room.RoomDatabase.Builder;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import androidx.sqlite.db.SupportSQLiteOpenHelper.Factory;
import android.content.Context;
// 工具类,提供创建数据库实例的静态方法
public class Room {
// 创建数据库构建器
public static <T extends RoomDatabase> Builder<T> databaseBuilder(Context context,
Class<T> klass,
String name) {
return new Builder<>(context, klass, name);
}
// 创建内存数据库构建器
public static <T extends RoomDatabase> Builder<T> inMemoryDatabaseBuilder(Context context,
Class<T> klass) {
return new Builder<>(context, klass, null);
}
// 数据库构建器类
public static class Builder<T extends RoomDatabase> {
private final Context mContext;
private final Class<T> mKlass;
private final String mName;
private Factory mOpenHelperFactory;
private RoomDatabase.Callback mCallback;
private boolean mAllowMainThreadQueries;
private boolean mJournalMode;
public Builder(Context context, Class<T> klass, String name) {
mContext = context;
mKlass = klass;
mName = name;
}
// 设置数据库辅助类工厂
public Builder<T> openHelperFactory(Factory factory) {
mOpenHelperFactory = factory;
return this;
}
// 设置数据库回调
public Builder<T> addCallback(RoomDatabase.Callback callback) {
if (mCallback == null) {
mCallback = callback;
} else {
mCallback = new CompositeCallback(mCallback, callback);
}
return this;
}
// 设置是否允许在主线程进行数据库查询
public Builder<T> allowMainThreadQueries() {
mAllowMainThreadQueries = true;
return this;
}
// 设置日志模式
public Builder<T> setJournalMode(boolean journalMode) {
mJournalMode = journalMode;
return this;
}
// 构建数据库实例
public T build() {
if (mOpenHelperFactory == null) {
mOpenHelperFactory = new FrameworkSQLiteOpenHelperFactory();
}
SupportSQLiteOpenHelper.Configuration configuration =
SupportSQLiteOpenHelper.Configuration.builder(mContext)
.name(mName)
.callback(new RoomOpenHelper(mKlass, mCallback))
.factory(mOpenHelperFactory)
.allowMainThreadQueries(mAllowMainThreadQueries)
.journalMode(mJournalMode)
.build();
T database = RoomDatabase.newInstance(mContext, mKlass, configuration);
if (mCallback != null) {
database.registerCallback(mCallback);
}
return database;
}
}
// 组合回调类
private static class CompositeCallback extends RoomDatabase.Callback {
private final RoomDatabase.Callback mCallback1;
private final RoomDatabase.Callback mCallback2;
public CompositeCallback(RoomDatabase.Callback callback1,
RoomDatabase.Callback callback2) {
mCallback1 = callback1;
mCallback2 = callback2;
}
@Override
public void onCreate(SupportSQLiteDatabase db) {
mCallback1.onCreate(db);
mCallback2.onCreate(db);
}
@Override
public void onOpen(SupportSQLiteDatabase db) {
mCallback1.onOpen(db);
mCallback2.onOpen(db);
}
@Override
public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
mCallback1.onUpgrade(db, oldVersion, newVersion);
mCallback2.onUpgrade(db, oldVersion, newVersion);
}
@Override
public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
mCallback1.onDowngrade(db, oldVersion, newVersion);
mCallback2.onDowngrade(db, oldVersion, newVersion);
}
}
}
Room
类提供了 databaseBuilder
和 inMemoryDatabaseBuilder
两个静态方法,用于创建数据库构建器。Builder
类用于配置数据库的相关信息,如数据库辅助类工厂、数据库回调、是否允许在主线程进行数据库查询等。build
方法用于构建数据库实例。在上述 DatabaseManager
类中,使用 Room.databaseBuilder
方法创建了一个数据库实例。
六、类型转换器分析
6.1 类型转换器的作用
在 Room 中,实体类的字段类型必须是 SQLite 支持的基本类型(如 int
、long
、String
等)。如果实体类的字段类型不是基本类型,就需要使用类型转换器将其转换为基本类型进行存储和读取。
java
import androidx.room.TypeConverter;
import java.util.Date;
// 类型转换器类
public class DateConverter {
// 将 Date 类型转换为 Long 类型
@TypeConverter
public static Long toTimestamp(Date date) {
return date == null ? null : date.getTime();
}
// 将 Long 类型转换为 Date 类型
@TypeConverter
public static Date toDate(Long timestamp) {
return timestamp == null ? null : new Date(timestamp);
}
}
6.1.1 源码分析
类型转换器通过 @TypeConverter
注解标记的方法实现。在上述 DateConverter
类中,toTimestamp
方法将 Date
类型转换为 Long
类型,toDate
方法将 Long
类型转换为 Date
类型。在使用类型转换器时,需要在 @Database
注解中指定类型转换器类。
java
import androidx.room.Database;
import androidx.room.RoomDatabase;
import androidx.room.TypeConverters;
// 使用 @Database 注解定义数据库类,并指定类型转换器类
@Database(entities = {User.class}, version = 1)
@TypeConverters({DateConverter.class})
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
在编译时,Room 编译器会处理类型转换器,将实体类中的非基本类型字段转换为基本类型进行存储和读取。
七、事务处理分析
7.1 事务的作用
在数据库操作中,事务用于确保一组操作要么全部成功,要么全部失败。Room 框架提供了事务处理的支持。
java
import androidx.room.RoomDatabase;
import androidx.room.Transaction;
// 在 DAO 接口中定义事务方法
@Dao
public interface UserDao {
// 插入用户
@Insert
void insert(User user);
// 更新用户
@Query("UPDATE users SET name = :name WHERE id = :id")
void updateUser(int id, String name);
// 定义事务方法
@Transaction
default void insertAndUpdate(User user, int updateId, String newName) {
insert(user);
updateUser(updateId, newName);
}
}
7.1.1 源码分析
@Transaction
注解用于标记事务方法。在上述 UserDao
接口中,insertAndUpdate
方法使用 @Transaction
注解标记,该方法包含了插入和更新两个操作。在编译时,Room 编译器会生成相应的代码,将这两个操作封装在一个事务中执行。
java
// 生成的事务方法实现
@Override
public void insertAndUpdate(User user, int updateId, String newName) {
db.beginTransaction();
try {
insert(user);
updateUser(updateId, newName);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
在执行事务方法时,会先调用 beginTransaction
方法开始事务,然后执行事务中的操作,最后调用 setTransactionSuccessful
方法标记事务成功,调用 endTransaction
方法结束事务。如果在事务执行过程中发生异常,setTransactionSuccessful
方法不会被调用,事务会自动回滚。
八、公共模块的编译时处理分析
8.1 编译时注解处理器
Room 框架使用编译时注解处理器来处理 @Entity
、@Dao
、@Query
等注解。编译时注解处理器会在编译阶段扫描代码中的注解,并根据注解信息生成相应的代码。
java
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;
import java.util.Set;
// 编译时注解处理器类
public class RoomProcessor implements XProcessor {
private final XProcessingEnv mEnv;
public RoomProcessor(ProcessingEnvironment env) {
mEnv = XProcessingEnv.create(env);
}
@Override
public Set<String> getSupportedAnnotationTypes() {
// 返回支持的注解类型
return Set.of(
"androidx.room.Entity",
"androidx.room.Dao",
"androidx.room.Query"
);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// 处理注解
for (TypeElement annotation : annotations) {
if (annotation.getQualifiedName().contentEquals("androidx.room.Entity")) {
// 处理 @Entity 注解
processEntityAnnotation(roundEnv, annotation);
} else if (annotation.getQualifiedName().contentEquals("androidx.room.Dao")) {
// 处理 @Dao 注解
processDaoAnnotation(roundEnv, annotation);
} else if (annotation.getQualifiedName().contentEquals("androidx.room.Query")) {
// 处理 @Query 注解
processQueryAnnotation(roundEnv, annotation);
}
}
return true;
}
private void processEntityAnnotation(RoundEnvironment roundEnv, TypeElement annotation) {
// 处理 @Entity 注解的逻辑
Set<? extends Element> entities = roundEnv.getElementsAnnotatedWith(annotation);
for (Element entity : entities) {
// 生成数据库表的 SQL 语句
generateTableSql(entity);
}
}
private void processDaoAnnotation(RoundEnvironment roundEnv, TypeElement annotation) {
// 处理 @Dao 注解的逻辑
Set<? extends Element> daos = roundEnv.getElementsAnnotatedWith(annotation);
for (Element dao : daos) {
// 生成 DAO 接口的实现类
generateDaoImplementation(dao);
}
8.1.1 processEntityAnnotation
方法详细分析
java
private void processEntityAnnotation(RoundEnvironment roundEnv, TypeElement annotation) {
// 获取所有被 @Entity 注解标记的类元素
Set<? extends Element> entities = roundEnv.getElementsAnnotatedWith(annotation);
for (Element entity : entities) {
// 获取 @Entity 注解实例
Entity entityAnnotation = entity.getAnnotation(Entity.class);
String tableName = entityAnnotation.tableName();
if (tableName.isEmpty()) {
// 如果未指定表名,使用类名作为表名
tableName = entity.getSimpleName().toString();
}
StringBuilder createTableSql = new StringBuilder();
createTableSql.append("CREATE TABLE IF NOT EXISTS `").append(tableName).append("` (");
// 获取类的所有字段元素
List<? extends Element> fields = entity.getEnclosedElements().stream()
.filter(e -> e.getKind() == ElementKind.FIELD)
.collect(Collectors.toList());
boolean isFirstColumn = true;
for (Element field : fields) {
if (!isFirstColumn) {
createTableSql.append(", ");
}
isFirstColumn = false;
String columnName = field.getSimpleName().toString();
String columnType = getColumnType(field.asType());
// 检查是否为主键
PrimaryKey primaryKeyAnnotation = field.getAnnotation(PrimaryKey.class);
if (primaryKeyAnnotation != null) {
createTableSql.append("`").append(columnName).append("` ").append(columnType)
.append(" PRIMARY KEY");
if (primaryKeyAnnotation.autoGenerate()) {
createTableSql.append(" AUTOINCREMENT");
}
} else {
createTableSql.append("`").append(columnName).append("` ").append(columnType);
}
}
createTableSql.append(")");
// 生成数据库表的 SQL 语句
System.out.println("Generated SQL for table " + tableName + ": " + createTableSql.toString());
}
}
private String getColumnType(TypeMirror typeMirror) {
if (typeMirror.toString().equals("int") || typeMirror.toString().equals("java.lang.Integer")) {
return "INTEGER";
} else if (typeMirror.toString().equals("long") || typeMirror.toString().equals("java.lang.Long")) {
return "INTEGER";
} else if (typeMirror.toString().equals("String")) {
return "TEXT";
} else if (typeMirror.toString().equals("double") || typeMirror.toString().equals("java.lang.Double")) {
return "REAL";
} else if (typeMirror.toString().equals("float") || typeMirror.toString().equals("java.lang.Float")) {
return "REAL";
}
// 可以添加更多类型的映射
return "TEXT";
}
在 processEntityAnnotation
方法中,首先获取所有被 @Entity
注解标记的类元素。对于每个实体类,获取 @Entity
注解中的表名,如果未指定表名,则使用类名作为表名。接着,遍历实体类的所有字段,根据字段类型和是否为主键生成相应的 SQL 列定义。最后,拼接出完整的 CREATE TABLE
SQL 语句。getColumnType
方法用于将 Java 类型映射为 SQLite 类型。
8.1.2 processDaoAnnotation
方法详细分析
java
private void processDaoAnnotation(RoundEnvironment roundEnv, TypeElement annotation) {
// 获取所有被 @Dao 注解标记的接口元素
Set<? extends Element> daos = roundEnv.getElementsAnnotatedWith(annotation);
for (Element dao : daos) {
String daoClassName = dao.getSimpleName().toString();
String implementationClassName = daoClassName + "_Impl";
StringBuilder classBuilder = new StringBuilder();
classBuilder.append("public class ").append(implementationClassName)
.append(" implements ").append(daoClassName).append(" {\n");
classBuilder.append(" private final RoomDatabase mDatabase;\n");
classBuilder.append(" public ").append(implementationClassName)
.append("(RoomDatabase database) {\n");
classBuilder.append(" this.mDatabase = database;\n");
classBuilder.append(" }\n");
// 获取接口的所有方法元素
List<? extends Element> methods = dao.getEnclosedElements().stream()
.filter(e -> e.getKind() == ElementKind.METHOD)
.collect(Collectors.toList());
for (Element method : methods) {
String methodName = method.getSimpleName().toString();
String returnType = method.asType().toString();
List<? extends VariableElement> parameters = ((ExecutableElement) method).getParameters();
Insert insertAnnotation = method.getAnnotation(Insert.class);
if (insertAnnotation != null) {
// 处理插入方法
classBuilder.append(" @Override\n");
classBuilder.append(" public void ").append(methodName).append("(");
for (int i = 0; i < parameters.size(); i++) {
VariableElement param = parameters.get(i);
classBuilder.append(param.asType().toString()).append(" ").append(param.getSimpleName());
if (i < parameters.size() - 1) {
classBuilder.append(", ");
}
}
classBuilder.append(") {\n");
classBuilder.append(" mDatabase.beginTransaction();\n");
classBuilder.append(" try {\n");
for (VariableElement param : parameters) {
String entityClassName = param.asType().toString();
String tableName = getTableNameFromEntity(entityClassName);
StringBuilder insertSql = new StringBuilder();
insertSql.append("INSERT INTO `").append(tableName).append("` (");
// 获取实体类的字段
TypeElement entityElement = (TypeElement) mEnv.getElementUtils().getTypeElement(entityClassName);
List<? extends Element> fields = entityElement.getEnclosedElements().stream()
.filter(e -> e.getKind() == ElementKind.FIELD)
.collect(Collectors.toList());
boolean isFirstColumn = true;
for (Element field : fields) {
if (!isFirstColumn) {
insertSql.append(", ");
}
isFirstColumn = false;
insertSql.append("`").append(field.getSimpleName()).append("`");
}
insertSql.append(") VALUES (");
isFirstColumn = true;
for (int i = 0; i < fields.size(); i++) {
if (!isFirstColumn) {
insertSql.append(", ");
}
isFirstColumn = false;
insertSql.append("?");
}
insertSql.append(")");
classBuilder.append(" mDatabase.compileStatement("").append(insertSql.toString()).append("").executeInsert();\n");
}
classBuilder.append(" mDatabase.setTransactionSuccessful();\n");
classBuilder.append(" } finally {\n");
classBuilder.append(" mDatabase.endTransaction();\n");
classBuilder.append(" }\n");
classBuilder.append(" }\n");
}
Query queryAnnotation = method.getAnnotation(Query.class);
if (queryAnnotation != null) {
// 处理查询方法
String sql = queryAnnotation.value();
classBuilder.append(" @Override\n");
classBuilder.append(" public ").append(returnType).append(" ").append(methodName).append("(");
for (int i = 0; i < parameters.size(); i++) {
VariableElement param = parameters.get(i);
classBuilder.append(param.asType().toString()).append(" ").append(param.getSimpleName());
if (i < parameters.size() - 1) {
classBuilder.append(", ");
}
}
classBuilder.append(") {\n");
classBuilder.append(" Cursor cursor = mDatabase.query("").append(sql).append("", new String[]{");
for (int i = 0; i < parameters.size(); i++) {
VariableElement param = parameters.get(i);
classBuilder.append(param.getSimpleName());
if (i < parameters.size() - 1) {
classBuilder.append(", ");
}
}
classBuilder.append("});\n");
classBuilder.append(" try {\n");
if (returnType.startsWith("java.util.List")) {
// 返回列表类型
String entityClassName = returnType.substring(returnType.indexOf("<") + 1, returnType.length() - 1);
classBuilder.append(" List<").append(entityClassName).append("> result = new ArrayList<>();\n");
classBuilder.append(" while (cursor.moveToNext()) {\n");
classBuilder.append(" ").append(entityClassName).append(" item = new ").append(entityClassName).append("();\n");
// 获取实体类的字段
TypeElement entityElement = (TypeElement) mEnv.getElementUtils().getTypeElement(entityClassName);
List<? extends Element> fields = entityElement.getEnclosedElements().stream()
.filter(e -> e.getKind() == ElementKind.FIELD)
.collect(Collectors.toList());
for (Element field : fields) {
String fieldName = field.getSimpleName().toString();
String columnType = getColumnType(field.asType());
if (columnType.equals("INTEGER")) {
classBuilder.append(" item.set").append(capitalize(fieldName)).append("(cursor.getInt(cursor.getColumnIndex("").append(fieldName).append("")));\n");
} else if (columnType.equals("TEXT")) {
classBuilder.append(" item.set").append(capitalize(fieldName)).append("(cursor.getString(cursor.getColumnIndex("").append(fieldName).append("")));\n");
}
// 可以添加更多类型的处理
}
classBuilder.append(" result.add(item);\n");
classBuilder.append(" }\n");
classBuilder.append(" return result;\n");
} else {
// 返回单个实体类型
String entityClassName = returnType;
classBuilder.append(" ").append(entityClassName).append(" item = null;\n");
if (cursor.moveToNext()) {
classBuilder.append(" item = new ").append(entityClassName).append("();\n");
// 获取实体类的字段
TypeElement entityElement = (TypeElement) mEnv.getElementUtils().getTypeElement(entityClassName);
List<? extends Element> fields = entityElement.getEnclosedElements().stream()
.filter(e -> e.getKind() == ElementKind.FIELD)
.collect(Collectors.toList());
for (Element field : fields) {
String fieldName = field.getSimpleName().toString();
String columnType = getColumnType(field.asType());
if (columnType.equals("INTEGER")) {
classBuilder.append(" item.set").append(capitalize(fieldName)).append("(cursor.getInt(cursor.getColumnIndex("").append(fieldName).append("")));\n");
} else if (columnType.equals("TEXT")) {
classBuilder.append(" item.set").append(capitalize(fieldName)).append("(cursor.getString(cursor.getColumnIndex("").append(fieldName).append("")));\n");
}
// 可以添加更多类型的处理
}
}
classBuilder.append(" return item;\n");
}
classBuilder.append(" } finally {\n");
classBuilder.append(" cursor.close();\n");
classBuilder.append(" }\n");
classBuilder.append(" }\n");
}
}
classBuilder.append("}\n");
// 生成 DAO 接口的实现类
System.out.println("Generated implementation class for " + daoClassName + ": " + classBuilder.toString());
}
}
private String getTableNameFromEntity(String entityClassName) {
TypeElement entityElement = (TypeElement) mEnv.getElementUtils().getTypeElement(entityClassName);
Entity entityAnnotation = entityElement.getAnnotation(Entity.class);
String tableName = entityAnnotation.tableName();
if (tableName.isEmpty()) {
tableName = entityElement.getSimpleName().toString();
}
return tableName;
}
private String capitalize(String str) {
if (str == null || str.isEmpty()) {
return str;
}
return Character.toUpperCase(str.charAt(0)) + str.substring(1);
}
在 processDaoAnnotation
方法中,首先获取所有被 @Dao
注解标记的接口元素。对于每个 DAO 接口,生成一个实现类。遍历接口的所有方法,根据方法上的注解(如 @Insert
、@Query
)生成相应的实现代码。对于插入方法,生成插入 SQL 语句并在事务中执行;对于查询方法,生成查询 SQL 语句并根据返回类型处理查询结果。
8.2 编译时生成的代码使用分析
8.2.1 生成的数据库表创建代码使用
在应用启动时,Room 会根据编译时生成的 SQL 语句创建数据库表。例如,在 RoomDatabase
的 onCreate
回调中执行这些 SQL 语句:
java
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
private static AppDatabase INSTANCE;
public static AppDatabase getInstance(Context context) {
if (INSTANCE == null) {
synchronized (AppDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, "app-database")
.addCallback(new Callback() {
@Override
public void onCreate(SupportSQLiteDatabase db) {
super.onCreate(db);
// 执行生成的 SQL 语句创建表
db.execSQL("CREATE TABLE IF NOT EXISTS `users` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `age` INTEGER)");
}
})
.build();
}
}
}
return INSTANCE;
}
}
8.2.2 生成的 DAO 实现类使用
在应用中,通过 RoomDatabase
获取 DAO 实例并调用其方法:
java
AppDatabase database = AppDatabase.getInstance(context);
UserDao userDao = database.userDao();
User user = new User("John", 25);
userDao.insert(user);
List<User> users = userDao.getAllUsers();
这里的 userDao.insert
和 userDao.getAllUsers
方法会调用编译时生成的 DAO 实现类中的相应方法。
九、公共模块的性能优化分析
9.1 预编译 SQL 语句
在生成的 DAO 实现类中,对于插入和查询等操作,使用预编译的 SQL 语句。例如,在插入方法中:
java
mDatabase.compileStatement("INSERT INTO `users` (`name`, `age`) VALUES (?, ?)").executeInsert();
预编译 SQL 语句可以提高性能,因为数据库只需要解析一次 SQL 语句,后续执行时只需要绑定参数即可。
9.2 事务处理优化
在插入和更新等操作中,使用事务处理。例如,在插入多个实体时:
java
mDatabase.beginTransaction();
try {
for (User user : users) {
mDatabase.compileStatement("INSERT INTO `users` (`name`, `age`) VALUES (?, ?)").executeInsert();
}
mDatabase.setTransactionSuccessful();
} finally {
mDatabase.endTransaction();
}
事务处理可以减少数据库的 I/O 操作,提高插入和更新的性能。
9.3 缓存机制
Room 框架内部可能使用了缓存机制,例如对于查询结果的缓存。当多次执行相同的查询时,如果数据没有发生变化,可能会直接从缓存中获取结果,而不需要再次查询数据库,从而提高查询性能。
十、公共模块的扩展性分析
10.1 自定义注解处理器
开发者可以自定义编译时注解处理器,扩展 Room 框架的功能。例如,可以定义一个新的注解,用于实现一些特定的数据库操作。
java
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 CustomQuery {
String value();
}
然后在自定义的注解处理器中处理这个注解:
java
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Set;
// 自定义注解处理器
public class CustomQueryProcessor extends AbstractProcessor {
@Override
public Set<String> getSupportedAnnotationTypes() {
return Set.of("com.example.CustomQuery");
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (TypeElement annotation : annotations) {
if (annotation.getQualifiedName().contentEquals("com.example.CustomQuery")) {
Set<? extends Element> methods = roundEnv.getElementsAnnotatedWith(annotation);
for (Element method : methods) {
CustomQuery customQueryAnnotation = method.getAnnotation(CustomQuery.class);
String sql = customQueryAnnotation.value();
// 处理自定义查询注解的逻辑
System.out.println("Custom query SQL: " + sql);
}
}
}
return true;
}
}
10.2 自定义类型转换器
开发者可以自定义类型转换器,支持更多的数据类型。例如,支持自定义的枚举类型:
java
import androidx.room.TypeConverter;
// 自定义类型转换器
public class EnumConverter {
@TypeConverter
public static MyEnum toEnum(String value) {
return value == null ? null : MyEnum.valueOf(value);
}
@TypeConverter
public static String fromEnum(MyEnum enumValue) {
return enumValue == null ? null : enumValue.name();
}
}
然后在 @Database
注解中指定自定义的类型转换器:
java
@Database(entities = {MyEntity.class}, version = 1)
@TypeConverters({EnumConverter.class})
public abstract class MyDatabase extends RoomDatabase {
public abstract MyDao myDao();
}
十一、公共模块的兼容性分析
11.1 与不同 Android 版本的兼容性
Room 框架的公共模块在设计上考虑了与不同 Android 版本的兼容性。例如,它使用了 SupportSQLiteOpenHelper
接口,该接口是 Android 支持库提供的,确保在不同版本的 Android 系统上都能正常使用 SQLite 数据库。同时,Room 框架会根据 Android 系统的版本进行一些优化和适配,例如在较新的 Android 版本上可能会使用更高效的数据库操作方法。
11.2 与其他 Jetpack 组件的兼容性
Room 框架是 Android Jetpack 组件的一部分,与其他 Jetpack 组件(如 LiveData、ViewModel、WorkManager 等)具有良好的兼容性。例如,Room 可以与 LiveData 结合使用,当数据库中的数据发生变化时,LiveData 会自动更新,从而实现数据的实时同步。
java
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Query;
@Dao
public interface UserDao {
@Query("SELECT * FROM users")
LiveData<List<User>> getAllUsersLiveData();
}
在 ViewModel 中使用 LiveData:
java
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import android.app.Application;
import java.util.List;
public class UserViewModel extends AndroidViewModel {
private final UserDao userDao;
private final LiveData<List<User>> allUsers;
public UserViewModel(Application application) {
super(application);
AppDatabase database = AppDatabase.getInstance(application);
userDao = database.userDao();
allUsers = userDao.getAllUsersLiveData();
}
public LiveData<List<User>> getAllUsers() {
return allUsers;
}
}
在 Activity 或 Fragment 中观察 LiveData:
java
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import java.util.List;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
UserViewModel userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
userViewModel.getAllUsers().observe(this, new Observer<List<User>>() {
@Override
public void onChanged(List<User> users) {
// 数据更新时的处理逻辑
}
});
}
}
十二、总结
Android Room 框架的公共模块是整个框架的基础,它提供了注解、接口、工具类等核心组件,为数据库操作提供了统一的标准和规范。通过编译时注解处理器,Room 可以根据注解信息生成相应的代码,减少了开发者的工作量。同时,公共模块在性能优化、扩展性和兼容性方面都有很好的设计,使得 Room 框架在 Android 开发中得到了广泛的应用。开发者可以深入理解公共模块的源码,更好地使用 Room 框架进行数据持久化开发。