在 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();

如何在Room框架下注册onUpgrade回调及自定义DatabaseErrorHandler的更多相关文章

  1. Swift技术之如何在iOS 8下使用Swift设计一个自定义的输入法 (主要是NSLayoutConstraint 的使用)

    当前位置: > Swift新手入门 > Swift技术之如何在iOS 8下使用Swift设计一个自定义的输入法 时间:2014-09-10 16:49来源:未知 作者:啊成 举报 点击:5 ...

  2. JEECG(二) JEECG框架下调用webservice java springmvc maven 调用 webservice

    JEECG系列教程二 如何在JEECG框架下使用webservice 本文所使用的webservice是c#开发的 其实无论是什么语言开发的webservice用法都一样 java springmvc ...

  3. 在Spring Boot框架下使用WebSocket实现消息推送

    Spring Boot的学习持续进行中.前面两篇博客我们介绍了如何使用Spring Boot容器搭建Web项目(使用Spring Boot开发Web项目)以及怎样为我们的Project添加HTTPS的 ...

  4. 基于dubbo框架下的RPC通讯协议性能测试

    一.前言 Dubbo RPC服务框架支持丰富的传输协议.序列化方式等通讯相关的配置和扩展.dubbo执行一次RPC请求的过程大致如下:消费者(Consumer)向注册中心(Registry)执行RPC ...

  5. 在Jena框架下基于MySQL数据库实现本体的存取操作

    在Jena框架下基于MySQL数据库实现本体的存取操作 转自:http://blog.csdn.net/jtz_mpp/article/details/6224311 最近在做一个基于本体的管理系统. ...

  6. 如何在spring框架中解决多数据源的问题

    在我们的项目中遇到这样一个问题:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库.我们以往在spring和hibernate框架中总是配置一个数据源,因而sessi ...

  7. WPF Prism框架下基于MVVM模式的命令、绑定、事件

    Prism框架下的自定义路由事件和命令绑定 BaseCode XAML代码: <Button x:Class="IM.UI.CommandEx.PrismCommandEx" ...

  8. 在Spring Boot框架下使用WebSocket实现聊天功能

    上一篇博客我们介绍了在Spring Boot框架下使用WebSocket实现消息推送,消息推送是一对多,服务器发消息发送给所有的浏览器,这次我们来看看如何使用WebSocket实现消息的一对一发送,模 ...

  9. 依赖注入[7]: .NET Core DI框架[服务注册]

    包含服务注册信息的IServiceCollection对象最终被用来创建作为DI容器的IServiceProvider对象.服务注册就是创建出现相应的ServiceDescriptor对象并将其添加到 ...

随机推荐

  1. axios 之cancelToken原理以及使用

    看axios文档的时候发现cancelToken这个东东,这个是用来取消ajax请求的,一般原生的话用的是abort()这个方法.看到这玩意的第一感觉是用起来有点麻烦,但是看了内部实现,发现还是比较有 ...

  2. ORA-22922:nonexistent LOB value问题及listagg()函数

    1 现象及错误信息 在执行一次查询的过程,Oracle出现ORA-22922:nonexistent LOB value 的错误:根据提示,是在查询时没有找到lob对象: 2 问题分析 查看SQL,发 ...

  3. 搭建golang开发环境(1.14之后版本)

    Go语言1.14版本之后推荐使用go modules管理依赖,也不再需要把代码写在GOPATH目录下. 下载地址 Go官网下载地址:https://golang.org/dl/ Go官方镜像站(推荐) ...

  4. 反射(reflection),通过反射创建对象

    简单尝试: import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public cl ...

  5. Java产生指定范围内的随机日期

    要想产生指定范围内的随机日期,首先我们要指定一个范围,那么我们可以通过SImpleDateFormat格式化日期,然后再通过parse()方法设置日期,返回一个Date类型的日期对象,再转化为时间戳( ...

  6. pytest(4)-测试用例执行顺序

    前言 上一篇文章我们讲了在pytest中测试用例的命名规则,那么在pytest中又是以怎样的顺序执行测试用例的呢? 在unittest框架中,默认按照ACSII码的顺序加载测试用例并执行,顺序为:09 ...

  7. CoaXPress 接口相机的控制方法--1

    GenICam 介绍 简而言之,GenICam 定义了一个通用的相机接口,使得应用程序的编写.相机的控制可以与具体的型号解耦,这样就可以设计出通用的软件完成对不同相机的控制.我们实际使用的CoaXPr ...

  8. 03 前端基础之JavaScript

    目录 前端基础之JavaScript JavaScript JavaScript注释 变量与常量 基本数据类型 number类型 string类型 boolean类型 null与undefined类型 ...

  9. RISC-V 特权指令结构

    机器模式 机器模式(缩写为 M 模式,M-mode)是 RISC-V 中 hart(hardware thread,硬件线 程)可以执行的最高权限模式.在 M 模式下运行的 hart 对内存,I/O ...

  10. Spring 控制反转和依赖注入

    控制反转的类型 控制反转(IOC)旨在提供一种更简单的机制,来设置组件的依赖项,并在整个生命周期管理这些依赖项.通常,控制反转可以分成两种子类型:依赖注入(DI)和依赖查找(DL),这些子类型各自又可 ...