目录
源码解析目录
从Room源码看抽象与封装——SQLite的抽象
从Room源码看抽象与封装——数据库的创建
从Room源码看抽象与封装——数据库的升降级
从Room源码看抽象与封装——Dao
从Room源码看抽象与封装——数据流
前言
上一篇文章讲了Room抽象的前两个层次,接口层以及接口实现层,这篇文章会介绍最后一个层次,Room实现层。
前两个抽象层次其实是与Room无关的,因为它只是对于SQLite的抽象,理论上讲,我们可以基于前两个抽象层次构建出自己的ORM框架。不过,话又说回来,Room的这种抽象必然不是完全独立的,Room实现层操作数据库的一些细节还是体现在了头两个抽象层次中。
Room实现层还是有一些复杂性的,并且细节也很多,这篇文章会专注于数据库的创建流程。(源码版本androidx.room:room-runtime:2.1.0)
数据库的创建
上篇文章讲到,SupportSQLiteOpenHelper
是Room与SQLite建立联系的唯一途径,并且SupportSQLiteOpenHelper
还把这一过程拆分成了数据库的配置阶段(通过SupportSQLiteOpenHelper.Callback
)和数据库的使用阶段(一般是使用SupportSQLiteOpenHelper
的实现类FrameworkSQLiteOpenHelper
)。数据库的使用阶段已经有默认实现了,不用我们操心,我们要完成的就是如何实现SupportSQLiteOpenHelper.Callback
。那就先来回顾一下SupportSQLiteOpenHelper.Callback
:
public interface SupportSQLiteOpenHelper {
//...
abstract class Callback {
public final int version;
//必须提供数据库版本号
public Callback(int version) {
this.version = version;
}
/**
* Called when the database connection is being configured, to enable features such as
* write-ahead logging or foreign key support.
*/
public void onConfigure(SupportSQLiteDatabase db) {
}
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) {
throw new SQLiteException("Can't downgrade database from version "
+ oldVersion + " to " + newVersion);
}
public void onOpen(SupportSQLiteDatabase db) {
}
public void onCorruption(SupportSQLiteDatabase db) {
}
}
}
可以看出,我们必须实现的是onCreate
以及onUpgrade
方法,当然Callback
实际上是覆盖了数据库配置(指的是数据库打开之前)的全流程的,每个方法都有其回调的时机,也都有适用的场景,虽说我们必须实现onCreate
以及onUpgrade
方法,但并不是说别的方法就可以置之不理。
想想onCreate
方法适合完成什么样的工作,必然是建表工作;onUpgrade
方法就更不用说了,必然是数据库升级工作。这仅仅是从这些方法的调用时机上去分析的结果,为了更加明晰各个回调方法的职责,Room对Callback
进行了进一步抽象,明确了各个方法应该完成的内容:
//RoomOpenHelper实现了SupportSQLiteOpenHelper.Callback
public class RoomOpenHelper extends SupportSQLiteOpenHelper.Callback {
//Delegate类的定义就在下方
@NonNull
private final Delegate mDelegate;
@Override
public void onCreate(SupportSQLiteDatabase db) {
updateIdentity(db);
//onCreate必须完成的工作,建表
mDelegate.createAllTables(db);
//依然为其它需要再onCreate中完成的工作保留回调
mDelegate.onCreate(db);
}
@Override
public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
//伪代码,具体数据库升级内容之后会介绍
boolean migrated = false;
//配置了相应的数据库升级方法
if (has migrations) {
//升级前回调
mDelegate.onPreMigrate(db);
//升级数据库
migrate(db);
//验证数据库升级是否正确
mDelegate.validateMigration(db);
//升级后回调
mDelegate.onPostMigrate(db);
migrated = true;
}
//没有升级并且允许以重建表的形式升级的话(之前的数据会完全丢失)
if (!migrated && allowDestructiveMigration) {
//丢弃原有的所有数据库表
mDelegate.dropAllTables(db);
//创建新的表
mDelegate.createAllTables(db);
}
}
@Override
public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
//升降级是统一处理的
onUpgrade(db, oldVersion, newVersion);
}
@Override
public void onOpen(SupportSQLiteDatabase db) {
super.onOpen(db);
checkIdentity(db);
//数据库打开回调
mDelegate.onOpen(db);
// there might be too many configurations etc, just clear it.
mConfiguration = null;
}
/**
* 明确了SupportSQLiteOpenHelper.Callback的具体职责
* 正如前面看到的那样,Delegate中的各个方法会在RoomOpenHelper合适的时机被调用
*/
public abstract static class Delegate {
public final int version;
public Delegate(int version) {
this.version = version;
}
protected abstract void dropAllTables(SupportSQLiteDatabase database);
protected abstract void createAllTables(SupportSQLiteDatabase database);
protected abstract void onOpen(SupportSQLiteDatabase database);
protected abstract void onCreate(SupportSQLiteDatabase database);
//验证数据库升级的完整性
protected abstract void validateMigration(SupportSQLiteDatabase db);
//升级前
protected void onPreMigrate(SupportSQLiteDatabase database) {
}
//升级后
protected void onPostMigrate(SupportSQLiteDatabase database) {
}
}
}
既然SupportSQLiteOpenHelper.Callback
的onCreate
方法一定是需要建表的;onUpgrade
方法一定是需要升级数据库的(包括升级数据库的一些列步骤,升级及升级后的验证等等),那么为何不把这些内容以“接口”的形式规范下来。RoomOpenHelper
就是这么做的,SupportSQLiteOpenHelper.Callback
仅仅只是对于SQLiteOpenHelper
的抽象与封装,SupportSQLiteOpenHelper.Callback
强调的是SQLiteOpenHelper
在数据库使用之前的回调,而RoomOpenHelper.Delegate
则明确了在具体回调方法中的职责。不过也不用担心,RoomOpenHelper.Delegate
绝大部分内容Room都会通过注解处理器帮我们实现,并不需要我们完成太多内容。
需要特别注意,
RoomOpenHelper
这个名字很具有迷惑性,看名字似乎应该是个SupportSQLiteOpenHelper
,但是,其实它是个SupportSQLiteOpenHelper.Callback
。上篇文章讲过,SupportSQLiteOpenHelper
的实现类是FrameworkSQLiteOpenHelper
。
下面的问题就很明确了,RoomOpenHelper
是怎么创建的呢?我们可以拍着胸脯说必然是通过如下的方式创建的:
Room.databaseBuilder(
applicationContext,
AppDatabase::class.java,
"database-name"
).build()
这肯定是正确的,因为这是RoomDatabase创建的入口。但是从这个方法到RoomOpenHelper
,这中间的跨度似乎有点大。我们就从这个方法作为入口,看看最终是怎么创建出RoomOpenHelper
,进而建立起与底层SQLite的联系的。
从RoomDatabase.Builder到RoomOpenHelper
光看这个方法名Room.databaseBuilder().build()
也能猜得出来这是Builder(构建者)模式,事实也的确如此:
public class Room {
@NonNull
public static RoomDatabase.Builder databaseBuilder(
@NonNull Context context, @NonNull Class klass, @NonNull String name) {
//noinspection ConstantConditions
if (name == null || name.trim().length() == 0) {
throw new IllegalArgumentException("Cannot build a database with null or empty name."
+ " If you are trying to create an in memory database, use Room"
+ ".inMemoryDatabaseBuilder");
}
//返回的是RoomDatabase.Builder类,显然是RoomDatabase的构建者
return new RoomDatabase.Builder<>(context, klass, name);
}
//...
}
Room类上的静态方法databaseBuilder
主要是方便我们使用,实际上是RoomDatabase的创建时要通过其Builder的。
public abstract class RoomDatabase {
public static class Builder {
//...
Builder(@NonNull Context context, @NonNull Class klass, @Nullable String name) {
mContext = context;
mDatabaseClass = klass;
mName = name;
mJournalMode = JournalMode.AUTOMATIC;
mRequireMigration = true;
mMigrationContainer = new MigrationContainer();
}
//Builder上拥有诸多配置方法,这里统统省略
@NonNull
public T build() {
//对于我们没有设置的属性设置默认值
//对于我们设置的属性进行必要的验证等等
//注意到,我们为RoomDatabase设置的种种属性会汇集到一个叫DatabaseConfiguration的类中
DatabaseConfiguration configuration =
new DatabaseConfiguration(
mContext,
mName,
mFactory,
mMigrationContainer,
mCallbacks,
mAllowMainThreadQueries,
mJournalMode.resolve(mContext),
mQueryExecutor,
mTransactionExecutor,
mMultiInstanceInvalidation,
mRequireMigration,
mAllowDestructiveMigrationOnDowngrade,
mMigrationsNotRequiredFrom);
//这里的类型参数 T 即是我们自己定义的,扩展自RoomDatabase的数据库类型,上文中叫AppDatabase
T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
//调用RoomDatabase的init方法,传入的参数是DatabaseConfiguration,包含了我们对于数据库的种种设置
db.init(configuration);
return db;
}
}
}
RoomDatabase.Builder
是典型的Builder模式,通过Builder可以为数据库配置各种属性,这些属性最终被汇集到了一个叫DatabaseConfiguration
的类中。一般情况下,我们会在Builder的build方法中通过调用目标对象(这里是RoomDatabase)的构造函数来最终构建出目标对象。然而,这在RoomDatabase.Builder
中是不可行的,因为其Builder要构建的是我们自定义的,扩展自RoomDatabase
的对象(为了方便以下直接称这个类叫AppDatabase
),自然是不能提前确定其类型的,也不能调用它的构造函数(AppDatabase
是抽象类,根本不可能被构造出来)。上篇文章说了,Room的使用就是加各种注解,然后注解处理器会帮我们生成一些我们需要的类,这些类有一个特点,就是特别冗长,都是一些套路似的代码,非常适合使用注解处理器来生成。我们定义的AppDatabase
是个抽象类(也必须是个抽象类),自然需要一个实现类,这个实现类就是Room注解处理器帮我们生成的(别忘了AppDatabase
上是加了@Database
注解的),生成的对应的类名是AppDatabase_Impl
,就是原名加后缀_Impl
,包名跟我们定义的AppDatabase
相同。
有了上面这些背景知识,大概也可以猜出Room.getGeneratedImplementation
干了些什么。很简单,就是通过反射来创建出AppDatabase_Impl
的对象。然后,我们看到调用了其上的init
方法完成了初始化。
我们终于来到RoomDatabase
了,来看看它的init
方法完成了哪些初始化工作:
public abstract class RoomDatabase {
@CallSuper
public void init(@NonNull DatabaseConfiguration configuration) {
//创建SupportSQLiteOpenHelper需要SupportSQLiteOpenHelper.Callback
//而这个SupportSQLiteOpenHelper.Callback就是RoomOpenHelper
mOpenHelper = createOpenHelper(configuration);
//...
}
@NonNull
protected abstract SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config);
//...
}
RoomDatabase
包含的内容是很多的,但是,如果把内容限定到本文讨论的“主线”中,相关的内容少多了。init
方法的最主要内容就是调用createOpenHelper
方法来创建SupportSQLiteOpenHelper
,而createOpenHelper
是个抽象方法,会在AppDatabase_Impl
中被实现。
扯了这么多,你可能都已经忘记了我们是怎么来到这里的。我给你缕缕。创建RoomDatabase
最关键是要实现它的抽象方法createOpenHelper
,createOpenHelper
返回一个SupportSQLiteOpenHelper
,Room中SupportSQLiteOpenHelper
的唯一实现类是FrameworkSQLiteOpenHelper
;而创建FrameworkSQLiteOpenHelper
又必须需要传入SupportSQLiteOpenHelper.Callback
,万幸Room帮我们实现了SupportSQLiteOpenHelper.Callback
,它是RoomOpenHelper
;RoomOpenHelper
通过明确回调方法的职责,又抽象出了RoomOpenHelper.Delegate
,也就是说,现在我们不需要再去关心SupportSQLiteOpenHelper.Callback
,只需要实现RoomOpenHelper.Delegate
就可以了;RoomOpenHelper.Delegate
中主要的内容是建表、升级、验证升级等,都是些繁琐且冗长的东西,这些都是注解处理器帮我们生成的;注解处理器生成了AppDatabase_Impl
类,是RoomDatabase
的具体实现,AppDatabase_Impl
在其createOpenHelper
方法中实例化了RoomOpenHelper.Delegate
。
希望没把你绕晕,总之呢,我们看一下AppDatabase_Impl
的实现就知道RoomOpenHelper.Delegate
是如何实现的。
public final class AppDatabase_Impl extends AppDatabase {
//...
@Override
protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(1) {
@Override
public void createAllTables(SupportSQLiteDatabase _db) {
//都是类似的建表SQL语句,由于是注解处理器生成的,不必担心出错
_db.execSQL("CREATE TABLE IF NOT EXISTS `Subject` (`id` INTEGER NOT NULL, `name` TEXT, `gradeId` INTEGER NOT NULL, PRIMARY KEY(`id`))");
}
@Override
public void dropAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("DROP TABLE IF EXISTS `Grade`");
}
@Override
protected void validateMigration(SupportSQLiteDatabase _db) {
//用于数据库升降级后的验证,保证升降级后表结构的正确性,非常非常的冗长
}
//...
}, /*代表数据库表结构的hash值*/"fb387e669d2f38d41af37b3394c05f6b");
final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
.name(configuration.name)
.callback(_openCallback)
.build();
//上篇文章讲过,创建SupportSQLiteOpenHelper是要通过工厂方法的,默认情况下返回的就是FrameworkSQLiteOpenHelper
final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
return _helper;
}
//...
}
AppDatabase_Impl
是注解处理器生成的,其中包含了特别冗长的RoomOpenHelper.Delegate
类的具体实现,至此,数据库的创建流程就走完了。
总结
Room抽象的前两层,接口层和接口实现层,把Room这整个抽象系统与原有的被抽象系统SQLite进行了隔离,在此之上,Room的实现层的种种其实就已经与SQLite无关了。我们看到,在Room实现层并没有仅仅“满足于”接口层这种对于SQLite较底层的抽象,而是在此基础之上进行进一步的抽象,最典型的就是RoomOpenHelper.Delegate
,通过明确SupportSQLiteOpenHelper.Callback
回调的职责,RoomOpenHelper.Delegate
抽象出了更加清晰明了的“接口”,对于创建数据库而言,整体的实现结构也变得更加清晰。