如何在Room框架下注冊onUpgrade回調及自定義DatabaseErrorHandler

  在 Android 中,Room 爲 SQLite 提供了高效穩定的抽象層,簡化開發流程。RoomDatabase.java 是初始化數據庫的重要構建組件,通過它我們可以添加RoomDatabase#Callback監聽,RoomDatabase#Callback提供了以下回調接口:

/**
 * Callback for {@link RoomDatabase}.
 */
public abstract static class Callback {

    /**
     * Called when the database is created for the first time. This is called after all the
     * tables are created.
     *
     * @param db The database.
     */
    public void onCreate(@NonNull SupportSQLiteDatabase db) {
    }

    /**
     * Called when the database has been opened.
     *
     * @param db The database.
     */
    public void onOpen(@NonNull SupportSQLiteDatabase db) {
    }

    /**
     * Called after the database was destructively migrated
     *
     * @param db The database.
     */
    public void onDestructiveMigration(@NonNull SupportSQLiteDatabase db){
    }
}

但是對於RoomDatabase#Callback提供的回調接口有時很難滿足我們的開發需求,因爲它沒有將數據庫打開過程的完整生命週期事件暴露出來。我們更需要的是SupportSQLiteOpenHelper#Callback開放的生命週期事件:

  • onConfigure 在數據庫創建或打開之前回調,在這可以添加一些數據庫能力,如打開 WAL 日誌模式
  • onCreate 數據庫首次創建時回調
  • onUpgrade 數據庫版本低於請求版本時回調,進行升級處理
  • onDowngrade 類似於 onUpgrade,數據庫版本高於請求版本時回調,進行降級處理
  • onOpen 數據庫連接打開後回調
  • onCorruption 在數據庫損壞時調用,默認刪除db及相關日誌文件

  Room框架中SupportSQLiteOpenHelper#Callback的實現類是RoomOpenHelper,幸運的是這個 callback 被注入到SupportSQLiteOpenHelper.Configuration中,並且 Room 允許我們使用SupportSQLiteOpenHelper.Configuration來提供自定義的SupportSQLiteOpenHelper。因此我們可以對FrameworkSQLiteOpenHelperRoomOpenHelper進行一層代理並開放出想要的生命週期回調。
值得一提的是Room框架下,FrameworkSQLiteOpenHelper使用SupportSQLiteOpenHelper#CallbackonCorruption方法來處理數據庫損壞的情況,而其處理策略是刪除db及其關聯的日誌文件。

OpenHelper(Context context, String name, final FrameworkSQLiteDatabase[] dbRef,
        final Callback callback) {
    super(context, name, null, callback.version,
            new DatabaseErrorHandler() {
                @Override
                public void onCorruption(SQLiteDatabase dbObj) {
                    callback.onCorruption(getWrappedDb(dbRef, dbObj));
                }
            });
    mCallback = callback;
    mDbRef = dbRef;
}

很多時候這並不是我們想要的處理方式,通過代理RoomOpenHelper,替換掉onCorruption的默認實現方式,這樣就實現了自定義 DatabaseErrorHandler

下面給出具體的代碼示例展示如何進行代理:

// 自定義 SupportSQLiteOpenHelper.Factory 
public class DecoratedOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
    @NonNull
    private final SupportSQLiteOpenHelper.Factory delegate;
    @Nullable
    private final SupportSQLiteOpenHelper.Callback customListener;

    public DecoratedOpenHelperFactory(@NonNull SupportSQLiteOpenHelper.Factory factory, @Nullable SupportSQLiteOpenHelper.Callback customListener) {
        this.delegate = factory;
        this.customListener = customListener;
    }

    @Override
    public SupportSQLiteOpenHelper create(@NonNull SupportSQLiteOpenHelper.Configuration configuration) {
        final SupportSQLiteOpenHelper.Configuration sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
                .name(configuration.name)
                .callback(new DecoratedCallback(configuration.callback, customListener))
                .build();
        return delegate.create(sqliteConfig);
    }
}

// 自定義 SupportSQLiteOpenHelper.Callback
public class DecoratedCallback extends SupportSQLiteOpenHelper.Callback {
    @NonNull
    private final SupportSQLiteOpenHelper.Callback delegate;
    @Nullable
    private final SupportSQLiteOpenHelper.Callback customListener;

    public DecoratedCallback(@NonNull SupportSQLiteOpenHelper.Callback supportSqLiteOpenHelperCallback, @Nullable SupportSQLiteOpenHelper.Callback customListener) {
        super(supportSqLiteOpenHelperCallback.version);
        this.delegate = supportSqLiteOpenHelperCallback;
        this.customListener = customListener;
    }

    @Override
    public void onCreate(@Nullable SupportSQLiteDatabase db) {
        delegate.onCreate(db);
        Optional.ofNullable(customListener).ifPresent(customListener -> customListener.onCreate(db));
    }

    @Override
    public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
        delegate.onUpgrade(db, oldVersion, newVersion);
        Optional.ofNullable(customListener).ifPresent(customListener -> customListener.onUpgrade(db, oldVersion, newVersion));
    }

    @Override
    public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
        delegate.onDowngrade(db, oldVersion, newVersion);
        Optional.ofNullable(customListener).ifPresent(customListener -> customListener.onDowngrade(db, oldVersion, newVersion));
    }

    @Override
    public void onOpen(SupportSQLiteDatabase db) {
        delegate.onOpen(db);
        Optional.ofNullable(customListener).ifPresent(customListener -> customListener.onOpen(db));
    }

    @Override
    public void onCorruption(SupportSQLiteDatabase db) {
        Optional.ofNullable(customListener).ifPresent(customListener -> customListener.onCorruption(db));
    }
}

最後,Room的構造使用如下:

Room.databaseBuilder(context, databaseName)
    .openHelperFactory(new DecoratedOpenHelperFactory(
        new FrameworkSQLiteOpenHelperFactory(), new SupportSQLiteOpenHelper.Callback() {
            @Override
            public void onCreate(SupportSQLiteDatabase db) {

            }

            @Override
            public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {

            }

            // etc
        }))
    .build();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章