让WCDB兼容最新版Room
先直接推荐我的开源小库:WCDBRoomX ,如果它的README就已经让你知道库的核心作用,这篇文章就不需要看了。
WCDB是腾讯微信团队开源的客户端数据库框架,拥有高性能和支持加密等重要特性,并且可用于Android、iOS、Windows、macOS等多个平台。 我们知道,原生的加密数据库框架SQLCipher和不加密的SQLite相比,性能差距还是很大的,加密会使得读写效率严重下降,而WCDB很好地兼顾了性能和安全问题。
对使用了Google官方Jetpack Room库的开发者,WCDB 1.x版本也提供了完美支持:Use WCDB with Room ,然而,WCDB 2.x版本后变成了一个纯ORM框架,虽然也支持Java、Kotlin等语言(本质上就是一层封装,底层接口都一样),但是暂时没有计划支持兼容Room,从官方文档看,2.x版本更纯粹,一套代码跨全平台,所以也不关注各平台的其他框架兼容了。
跟WCDB的开发者也聊了聊,他们团队对Room的支持兴趣不大:issues#1052 。其实浏览一下2.x版本的更新日志,看似开发团队是iOS研发主导,很多更新也和Android无关。
不过问题不大,1.x版本的性能和稳定性已经非常好,连微信客户端自己都用了好多年。唯一的一个小问题是,Room最新版本支持了 @Upsert 注解,如果还继续使用 com.tencent.wcdb:room:1.x
库的话,在插入数据时没问题,但在更新数据时会崩溃,抛出异常:SQLiteConstraintException
。
解决方案有两种:
- 摆烂,不再使用Room的Upsert注解,把Upsert拆成Insert和Update,这样需要改动很多现有代码。
- Read the fucking code,看看为什么崩溃。
如果你选择第一种方案,后面就不用看了,但是为什么不看看第二种方案呢?/狗头
其实Upsert的实现很简单,最终执行插入或更新操作的源码在 EntityUpsertAdapter :
1 | /** |
可以看出,每次Upsert操作都会先进行插入,如果数据已经存在,会抛异常,在catch捕获异常后,再进行更新数据的操作。所以理论上Room是可以捕获异常的,为什么集成WCDB之后就捕获不到了呢?
原因是WCDB对SQLite的很多类都重新进行了封装,一些基本的代码虽然没有改动,但是包名却变成了 com.tencent.wcdb.database
:
1 | package com.tencent.wcdb.database; |
而Room只能捕获原生的 android.database.sqlite
包下的异常。知道原因后,这就好改了,只需要改造 WCDBStatement
即可:
1 | import com.tencent.wcdb.database.SQLiteConstraintException; |
捕获WCDB自定义的异常后,转换一次,抛出原生SQLite库的异常,这样就能被Room的EntityUpsertAdapter捕获到,顺利兼容Upsert操作。
当然, 我已经把这些兼容处理好了,并且迁移了相关的依赖库为AndroidX,同时集成最新版本的SQLCipher,才有了:WCDBRoomX
可以删掉所有WCDB、SQLCipher、SQLite相关的依赖,直接引入WCDBRoomX即可:
1 | dependencies { |
快速集成:
1 | Room.databaseBuilder(...) |