目錄
源碼解析目錄
從Room源碼看抽象與封裝——SQLite的抽象
從Room源碼看抽象與封裝——數(shù)據(jù)庫(kù)的創(chuàng)建
從Room源碼看抽象與封裝——數(shù)據(jù)庫(kù)的升降級(jí)
從Room源碼看抽象與封裝——Dao
從Room源碼看抽象與封裝——數(shù)據(jù)流
前言
上一篇文章講了Room抽象的前兩個(gè)層次,接口層以及接口實(shí)現(xiàn)層,這篇文章會(huì)介紹最后一個(gè)層次,Room實(shí)現(xiàn)層。

前兩個(gè)抽象層次其實(shí)是與Room無(wú)關(guān)的,因?yàn)樗皇菍?duì)于SQLite的抽象,理論上講,我們可以基于前兩個(gè)抽象層次構(gòu)建出自己的ORM框架。不過(guò),話又說(shuō)回來(lái),Room的這種抽象必然不是完全獨(dú)立的,Room實(shí)現(xiàn)層操作數(shù)據(jù)庫(kù)的一些細(xì)節(jié)還是體現(xiàn)在了頭兩個(gè)抽象層次中。
Room實(shí)現(xiàn)層還是有一些復(fù)雜性的,并且細(xì)節(jié)也很多,這篇文章會(huì)專注于數(shù)據(jù)庫(kù)的創(chuàng)建流程。(源碼版本androidx.room:room-runtime:2.1.0)
數(shù)據(jù)庫(kù)的創(chuàng)建
上篇文章講到,SupportSQLiteOpenHelper是Room與SQLite建立聯(lián)系的唯一途徑,并且SupportSQLiteOpenHelper還把這一過(guò)程拆分成了數(shù)據(jù)庫(kù)的配置階段(通過(guò)SupportSQLiteOpenHelper.Callback)和數(shù)據(jù)庫(kù)的使用階段(一般是使用SupportSQLiteOpenHelper的實(shí)現(xiàn)類FrameworkSQLiteOpenHelper)。數(shù)據(jù)庫(kù)的使用階段已經(jīng)有默認(rèn)實(shí)現(xiàn)了,不用我們操心,我們要完成的就是如何實(shí)現(xiàn)SupportSQLiteOpenHelper.Callback。那就先來(lái)回顧一下SupportSQLiteOpenHelper.Callback:
public interface SupportSQLiteOpenHelper {
//...
abstract class Callback {
public final int version;
//必須提供數(shù)據(jù)庫(kù)版本號(hào)
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) {
}
}
}
可以看出,我們必須實(shí)現(xiàn)的是onCreate以及onUpgrade方法,當(dāng)然Callback實(shí)際上是覆蓋了數(shù)據(jù)庫(kù)配置(指的是數(shù)據(jù)庫(kù)打開(kāi)之前)的全流程的,每個(gè)方法都有其回調(diào)的時(shí)機(jī),也都有適用的場(chǎng)景,雖說(shuō)我們必須實(shí)現(xiàn)onCreate以及onUpgrade方法,但并不是說(shuō)別的方法就可以置之不理。
想想onCreate方法適合完成什么樣的工作,必然是建表工作;onUpgrade方法就更不用說(shuō)了,必然是數(shù)據(jù)庫(kù)升級(jí)工作。這僅僅是從這些方法的調(diào)用時(shí)機(jī)上去分析的結(jié)果,為了更加明晰各個(gè)回調(diào)方法的職責(zé),Room對(duì)Callback進(jìn)行了進(jìn)一步抽象,明確了各個(gè)方法應(yīng)該完成的內(nèi)容:
//RoomOpenHelper實(shí)現(xiàn)了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中完成的工作保留回調(diào)
mDelegate.onCreate(db);
}
@Override
public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
//偽代碼,具體數(shù)據(jù)庫(kù)升級(jí)內(nèi)容之后會(huì)介紹
boolean migrated = false;
//配置了相應(yīng)的數(shù)據(jù)庫(kù)升級(jí)方法
if (has migrations) {
//升級(jí)前回調(diào)
mDelegate.onPreMigrate(db);
//升級(jí)數(shù)據(jù)庫(kù)
migrate(db);
//驗(yàn)證數(shù)據(jù)庫(kù)升級(jí)是否正確
mDelegate.validateMigration(db);
//升級(jí)后回調(diào)
mDelegate.onPostMigrate(db);
migrated = true;
}
//沒(méi)有升級(jí)并且允許以重建表的形式升級(jí)的話(之前的數(shù)據(jù)會(huì)完全丟失)
if (!migrated && allowDestructiveMigration) {
//丟棄原有的所有數(shù)據(jù)庫(kù)表
mDelegate.dropAllTables(db);
//創(chuàng)建新的表
mDelegate.createAllTables(db);
}
}
@Override
public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
//升降級(jí)是統(tǒng)一處理的
onUpgrade(db, oldVersion, newVersion);
}
@Override
public void onOpen(SupportSQLiteDatabase db) {
super.onOpen(db);
checkIdentity(db);
//數(shù)據(jù)庫(kù)打開(kāi)回調(diào)
mDelegate.onOpen(db);
// there might be too many configurations etc, just clear it.
mConfiguration = null;
}
/**
* 明確了SupportSQLiteOpenHelper.Callback的具體職責(zé)
* 正如前面看到的那樣,Delegate中的各個(gè)方法會(huì)在RoomOpenHelper合適的時(shí)機(jī)被調(diào)用
*/
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);
//驗(yàn)證數(shù)據(jù)庫(kù)升級(jí)的完整性
protected abstract void validateMigration(SupportSQLiteDatabase db);
//升級(jí)前
protected void onPreMigrate(SupportSQLiteDatabase database) {
}
//升級(jí)后
protected void onPostMigrate(SupportSQLiteDatabase database) {
}
}
}
既然SupportSQLiteOpenHelper.Callback的onCreate方法一定是需要建表的;onUpgrade方法一定是需要升級(jí)數(shù)據(jù)庫(kù)的(包括升級(jí)數(shù)據(jù)庫(kù)的一些列步驟,升級(jí)及升級(jí)后的驗(yàn)證等等),那么為何不把這些內(nèi)容以“接口”的形式規(guī)范下來(lái)。RoomOpenHelper就是這么做的,SupportSQLiteOpenHelper.Callback僅僅只是對(duì)于SQLiteOpenHelper的抽象與封裝,SupportSQLiteOpenHelper.Callback強(qiáng)調(diào)的是SQLiteOpenHelper在數(shù)據(jù)庫(kù)使用之前的回調(diào),而RoomOpenHelper.Delegate則明確了在具體回調(diào)方法中的職責(zé)。不過(guò)也不用擔(dān)心,RoomOpenHelper.Delegate絕大部分內(nèi)容Room都會(huì)通過(guò)注解處理器幫我們實(shí)現(xiàn),并不需要我們完成太多內(nèi)容。
需要特別注意,
RoomOpenHelper這個(gè)名字很具有迷惑性,看名字似乎應(yīng)該是個(gè)SupportSQLiteOpenHelper,但是,其實(shí)它是個(gè)SupportSQLiteOpenHelper.Callback。上篇文章講過(guò),SupportSQLiteOpenHelper的實(shí)現(xiàn)類是FrameworkSQLiteOpenHelper。
下面的問(wèn)題就很明確了,RoomOpenHelper是怎么創(chuàng)建的呢?我們可以拍著胸脯說(shuō)必然是通過(guò)如下的方式創(chuàng)建的:
Room.databaseBuilder(
applicationContext,
AppDatabase::class.java,
"database-name"
).build()
這肯定是正確的,因?yàn)檫@是RoomDatabase創(chuàng)建的入口。但是從這個(gè)方法到RoomOpenHelper,這中間的跨度似乎有點(diǎn)大。我們就從這個(gè)方法作為入口,看看最終是怎么創(chuàng)建出RoomOpenHelper,進(jìn)而建立起與底層SQLite的聯(lián)系的。
從RoomDatabase.Builder到RoomOpenHelper
光看這個(gè)方法名Room.databaseBuilder().build()也能猜得出來(lái)這是Builder(構(gòu)建者)模式,事實(shí)也的確如此:
public class Room {
@NonNull
public static <T extends RoomDatabase> RoomDatabase.Builder<T> databaseBuilder(
@NonNull Context context, @NonNull Class<T> 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的構(gòu)建者
return new RoomDatabase.Builder<>(context, klass, name);
}
//...
}
Room類上的靜態(tài)方法databaseBuilder主要是方便我們使用,實(shí)際上是RoomDatabase的創(chuàng)建時(shí)要通過(guò)其Builder的。
public abstract class RoomDatabase {
public static class Builder<T extends RoomDatabase> {
//...
Builder(@NonNull Context context, @NonNull Class<T> klass, @Nullable String name) {
mContext = context;
mDatabaseClass = klass;
mName = name;
mJournalMode = JournalMode.AUTOMATIC;
mRequireMigration = true;
mMigrationContainer = new MigrationContainer();
}
//Builder上擁有諸多配置方法,這里統(tǒng)統(tǒng)省略
@NonNull
public T build() {
//對(duì)于我們沒(méi)有設(shè)置的屬性設(shè)置默認(rèn)值
//對(duì)于我們?cè)O(shè)置的屬性進(jìn)行必要的驗(yàn)證等等
//注意到,我們?yōu)镽oomDatabase設(shè)置的種種屬性會(huì)匯集到一個(gè)叫DatabaseConfiguration的類中
DatabaseConfiguration configuration =
new DatabaseConfiguration(
mContext,
mName,
mFactory,
mMigrationContainer,
mCallbacks,
mAllowMainThreadQueries,
mJournalMode.resolve(mContext),
mQueryExecutor,
mTransactionExecutor,
mMultiInstanceInvalidation,
mRequireMigration,
mAllowDestructiveMigrationOnDowngrade,
mMigrationsNotRequiredFrom);
//這里的類型參數(shù) T 即是我們自己定義的,擴(kuò)展自RoomDatabase的數(shù)據(jù)庫(kù)類型,上文中叫AppDatabase
T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
//調(diào)用RoomDatabase的init方法,傳入的參數(shù)是DatabaseConfiguration,包含了我們對(duì)于數(shù)據(jù)庫(kù)的種種設(shè)置
db.init(configuration);
return db;
}
}
}
RoomDatabase.Builder是典型的Builder模式,通過(guò)Builder可以為數(shù)據(jù)庫(kù)配置各種屬性,這些屬性最終被匯集到了一個(gè)叫DatabaseConfiguration的類中。一般情況下,我們會(huì)在Builder的build方法中通過(guò)調(diào)用目標(biāo)對(duì)象(這里是RoomDatabase)的構(gòu)造函數(shù)來(lái)最終構(gòu)建出目標(biāo)對(duì)象。然而,這在RoomDatabase.Builder中是不可行的,因?yàn)槠銪uilder要構(gòu)建的是我們自定義的,擴(kuò)展自RoomDatabase的對(duì)象(為了方便以下直接稱這個(gè)類叫AppDatabase),自然是不能提前確定其類型的,也不能調(diào)用它的構(gòu)造函數(shù)(AppDatabase是抽象類,根本不可能被構(gòu)造出來(lái))。上篇文章說(shuō)了,Room的使用就是加各種注解,然后注解處理器會(huì)幫我們生成一些我們需要的類,這些類有一個(gè)特點(diǎn),就是特別冗長(zhǎng),都是一些套路似的代碼,非常適合使用注解處理器來(lái)生成。我們定義的AppDatabase是個(gè)抽象類(也必須是個(gè)抽象類),自然需要一個(gè)實(shí)現(xiàn)類,這個(gè)實(shí)現(xiàn)類就是Room注解處理器幫我們生成的(別忘了AppDatabase上是加了@Database注解的),生成的對(duì)應(yīng)的類名是AppDatabase_Impl,就是原名加后綴_Impl,包名跟我們定義的AppDatabase相同。
有了上面這些背景知識(shí),大概也可以猜出Room.getGeneratedImplementation干了些什么。很簡(jiǎn)單,就是通過(guò)反射來(lái)創(chuàng)建出AppDatabase_Impl的對(duì)象。然后,我們看到調(diào)用了其上的init方法完成了初始化。
我們終于來(lái)到RoomDatabase了,來(lái)看看它的init方法完成了哪些初始化工作:
public abstract class RoomDatabase {
@CallSuper
public void init(@NonNull DatabaseConfiguration configuration) {
//創(chuàng)建SupportSQLiteOpenHelper需要SupportSQLiteOpenHelper.Callback
//而這個(gè)SupportSQLiteOpenHelper.Callback就是RoomOpenHelper
mOpenHelper = createOpenHelper(configuration);
//...
}
@NonNull
protected abstract SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config);
//...
}
RoomDatabase包含的內(nèi)容是很多的,但是,如果把內(nèi)容限定到本文討論的“主線”中,相關(guān)的內(nèi)容少多了。init方法的最主要內(nèi)容就是調(diào)用createOpenHelper方法來(lái)創(chuàng)建SupportSQLiteOpenHelper,而createOpenHelper是個(gè)抽象方法,會(huì)在AppDatabase_Impl中被實(shí)現(xiàn)。
扯了這么多,你可能都已經(jīng)忘記了我們是怎么來(lái)到這里的。我給你縷縷。創(chuàng)建RoomDatabase最關(guān)鍵是要實(shí)現(xiàn)它的抽象方法createOpenHelper,createOpenHelper返回一個(gè)SupportSQLiteOpenHelper,Room中SupportSQLiteOpenHelper的唯一實(shí)現(xiàn)類是FrameworkSQLiteOpenHelper;而創(chuàng)建FrameworkSQLiteOpenHelper又必須需要傳入SupportSQLiteOpenHelper.Callback,萬(wàn)幸Room幫我們實(shí)現(xiàn)了SupportSQLiteOpenHelper.Callback,它是RoomOpenHelper;RoomOpenHelper通過(guò)明確回調(diào)方法的職責(zé),又抽象出了RoomOpenHelper.Delegate,也就是說(shuō),現(xiàn)在我們不需要再去關(guān)心SupportSQLiteOpenHelper.Callback,只需要實(shí)現(xiàn)RoomOpenHelper.Delegate就可以了;RoomOpenHelper.Delegate中主要的內(nèi)容是建表、升級(jí)、驗(yàn)證升級(jí)等,都是些繁瑣且冗長(zhǎng)的東西,這些都是注解處理器幫我們生成的;注解處理器生成了AppDatabase_Impl類,是RoomDatabase的具體實(shí)現(xiàn),AppDatabase_Impl在其createOpenHelper方法中實(shí)例化了RoomOpenHelper.Delegate。

希望沒(méi)把你繞暈,總之呢,我們看一下AppDatabase_Impl的實(shí)現(xiàn)就知道RoomOpenHelper.Delegate是如何實(shí)現(xiàn)的。
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語(yǔ)句,由于是注解處理器生成的,不必?fù)?dān)心出錯(cuò)
_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) {
//用于數(shù)據(jù)庫(kù)升降級(jí)后的驗(yàn)證,保證升降級(jí)后表結(jié)構(gòu)的正確性,非常非常的冗長(zhǎng)
}
//...
}, /*代表數(shù)據(jù)庫(kù)表結(jié)構(gòu)的hash值*/"fb387e669d2f38d41af37b3394c05f6b");
final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
.name(configuration.name)
.callback(_openCallback)
.build();
//上篇文章講過(guò),創(chuàng)建SupportSQLiteOpenHelper是要通過(guò)工廠方法的,默認(rèn)情況下返回的就是FrameworkSQLiteOpenHelper
final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
return _helper;
}
//...
}
AppDatabase_Impl是注解處理器生成的,其中包含了特別冗長(zhǎng)的RoomOpenHelper.Delegate類的具體實(shí)現(xiàn),至此,數(shù)據(jù)庫(kù)的創(chuàng)建流程就走完了。
總結(jié)

Room抽象的前兩層,接口層和接口實(shí)現(xiàn)層,把Room這整個(gè)抽象系統(tǒng)與原有的被抽象系統(tǒng)SQLite進(jìn)行了隔離,在此之上,Room的實(shí)現(xiàn)層的種種其實(shí)就已經(jīng)與SQLite無(wú)關(guān)了。我們看到,在Room實(shí)現(xiàn)層并沒(méi)有僅僅“滿足于”接口層這種對(duì)于SQLite較底層的抽象,而是在此基礎(chǔ)之上進(jìn)行進(jìn)一步的抽象,最典型的就是RoomOpenHelper.Delegate,通過(guò)明確SupportSQLiteOpenHelper.Callback回調(diào)的職責(zé),RoomOpenHelper.Delegate抽象出了更加清晰明了的“接口”,對(duì)于創(chuàng)建數(shù)據(jù)庫(kù)而言,整體的實(shí)現(xiàn)結(jié)構(gòu)也變得更加清晰。