• 如何在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();
    
  • 相关阅读:
    服务器实现端口转发的N种方式
    最新AI创作系统ChatGPT网站H5源码V2.6.4+搭建部署教程+支持GPT4.0+支持ai绘画(Midjourney)/支持Prompt预设应用
    域名系统与IP地址分配
    【马士兵】Python基础--13
    【Java 进阶篇】用JSTL玩转Java:了解JSTL常用标签
    Python可视化 | Python可视化进阶必备 - plotly
    新房装修,自带的线路有必更要换吗
    Cesium快速上手0-Cesium安装与基本介绍
    ChatGPT实战100例 - (18) 用事件风暴玩转DDD
    15、spring+mybatis+servlet框架搭建
  • 原文地址:https://www.cnblogs.com/ZhaoxiCheung/p/how-to-register-callback-for-database-with-room.html