從Room源碼看抽象與封裝——數(shù)據(jù)庫(kù)的創(chuàng)建

目錄

源碼解析目錄
從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)層。

Room抽象的前兩個(gè)層次

前兩個(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.CallbackonCreate方法一定是需要建表的;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ù)據(jù)庫(kù)創(chuàng)建流程

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)也變得更加清晰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容