微信移动端数据库组件 WCDB 系列:Android 特性篇(四)

作者:johnwhe

之前我们已经发过几篇 iOS 和修复的文章,Android 由于接口跟系统几乎一样,相信大家都比较熟悉,不熟悉用法也可以到 Android Developer 官网看一下。但是,我们也有一些特色功能和优化大家可能不容易注意到, 现在就单独拿出来说说。

加密接口

WCDB 使用了 SQLCipher 的 C 层库,但没有直接使用 SQLCipher Android 的封装层。SQLCipher Android 封装层中很多设置需要手写 PRAGMA 语句实现,比如设置 KDF 迭代次数(兼容老版本 SQLCipher DB)、设置 Page Size 等操作。

对于开发者来说,这需要了解 SQLCipher 底下的 PRAGMA 指令,更重要的是要搞清楚这些指令正确的调用顺序。 哪些是需要在设置 key 之前执行的?哪些是只有设置了 key 之后才生效的?开发者往往必须仔细查阅 SQLCipher 的文档来了解这些细节。

WCDB 对这个部分做了改进,封装了 SQLiteCipherSpec 用于设置加密参数,设置好了传给 SQLiteDatabase 工厂方法就好了,不需要考虑 PRAGMA 语法和调用顺序。

使用 SQLiteCipherSpec 另一个好处是,同样的结构可以传给 RepairKit 用于恢复损坏 DB,不需要两套 接口了。由于 RepairKit 底层不使用 PRAGMA,原来 hook 的形式不能满足需要。

另外,WCDB 将 String 类型的密码改为 byte[] 类型,可以支持非打印字符作为密码(比如 hash(user id) 方式),原来字符类型密码只要转换为 UTF-8 的 byte 数组即可,和 SQLCipher Android 兼容。

数据迁移

SQLCipher 提供了 sqlcipher_export SQL 函数用于导出数据到挂载的另一个 DB,可以用于数据迁移。 但这个函数用于 Android 的 SQLiteOpenHelper 并不方便。

SQLiteOpenHelper 主要帮助开发者做 Schema 版本管理,通过它打开 SQLite 数据库,会读取 user_version 字段来判断是否需要升级,并调用子类实现的 onCreate、onUpgrade 等接口来完成创建或升级操作。 sqlcipher_export 由于是导出而非导入,就跟 onCreate 等接口不搭了,因为要关闭原来的 DB, 打开老的 DB,执行 export 到新 DB,再重打开。

为了方便使用,WCDB 就做了扩展,将 sqlcipher_export 扩展为可以接受第二个参数表示从哪里导出, 从而实现了导入。

如此就可以不关闭原来的数据库实现数据导入,可以兼容 SQLiteOpenHelper 的接口了。详细可以看我们的 Sample。

全文搜索分词器与动态 ICU 加载

WCDB Android 自带了一个 FTS3/4 分词器,名为 mmicu,用于实现 SQLite 全文搜索。 分词器的使用与 SQLite 自带的 simple、icu 等分词器一样,创建虚拟表的时候带上名字即可:

MMICU 分词器与官方 ICU 分词器类似,但对中文(象形文字)分词以及 ICU 库加载做了特殊处理。 ICU 对中文的分词是基于词库的,Android 系统不同版本会附带不同版本的 ICU,捎带不同版本的中文 词库,当然也会带来不同的分词结果,这个对于统一产品体验是非常不利的。

另外,ICU 自带的中文词库并非非常完整,组词效果也一般,但若自带一个完整好用的词库, 又需要非常大的空间,这个空间会体现在 APK 体积上。最终,我们做了折中, 中文字全部单字成词,其他文字则使用 ICU 默认规则

ICU 还有一个严重的问题是动态库和自带的数据文件体积很大,超过 10MB,编译进 APK 里相当不划算, 最好能直接加载系统自带的 ICU 库。但加载系统库有另一个障碍:ICU 库不同版本会在函数名称后面 带上版本号后缀,直接编译时连接行不通。

为了克服这个障碍,WCDB 做了一个兼容层 icucompat,通过系统带的数据文件推断 ICU 版本, 通过 dlopen 动态加载不同的符号名称,然后通过宏来模拟直接调用方便开发。最终实现效果便是在不需要自带 ICU 库的前提下使用 ICU 库的断词、归一化等功能,为最终 APK 包省下 10MB 以上空间。

有了 ICU 兼容层,要实现 Android 框架自带的 ICU 相关功能就简单了,比如 LOCALIZED排序。 但是,WCDB 目前没有接入(主要是没有相关需求),有这方面需要的话,可以到我们的 GitHub 提 Issue 哦。

日志重定向与性能监控

SQLite 和 WCDB 框架在运行中会产生日志,这些日志默认会打印到系统日志(logcat),但这可能不是 所有开发者都希望的行为。比如担心日志里带有敏感信息,直接输出到系统不妥,或者希望将日志写到文件 用于上报和分析,WCDB 提供接口来完成日志重定向。

要实现高性能日志持久化,可以考虑使用我们 mars 里面的 xlog 组件。 微信终端跨平台组件 mars 系列(一) - 高性能日志模块xlog

WCDB 还提供了性能监控接口 SQLiteTrace,实现接口并绑定到 SQLiteDatabase 可以在每次 执行 SQL 语句或连接池拥堵的时候得到回调。

SQLiteDatabase 也开放了 dump 方法,可以打印出数据库的当前状态,包括连接池内所有连接 被持有的状态以及最近执行的 SQL 语句和耗时,对排查性能和死锁问题也有很大帮助。

优化 Cursor 实现

在 WCDB 发布时,我们的一篇文章上提到 Cursor 实现优化。对于 查询获取 Cursor → 遍历 → 关闭 这种简单的场景,我们通过 SQLiteDirectCursor 直接操作 SQLite 底层的查询,避免 CursorWindow 的重复分配带来的损耗。

可以看一下我们发布时的文章: 微信WCDB进化之路 - 开源与开始

需要注意的是 Direct Cursor 未关闭前会占用一个数据库连接,使用完需要尽快关闭,否则会一直占用 造成别的线程无法请求到连接。遍历 Cursor 过程中同一线程不做其他 DB 操作,遍历完关闭,配合 WAL 使用,是最佳实践。

本文来源于:WeMobileDev 微信公众号

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏转载gongluck的CSDN博客

cocos2dx 打灰机

#include "GamePlane.h" #include "PlaneSprite.h" #include "BulletNode.h" #include...

6866
来自专栏pangguoming

Spring Boot集成JasperReports生成PDF文档

由于工作需要,要实现后端根据模板动态填充数据生成PDF文档,通过技术选型,使用Ireport5.6来设计模板,结合JasperReports5.6工具库来调用渲...

1.4K7
来自专栏张善友的专栏

Mix 10 上的asp.net mvc 2的相关Session

Beyond File | New Company: From Cheesy Sample to Social Platform Scott Hansel...

2767
来自专栏Ceph对象存储方案

Luminous版本PG 分布调优

Luminous版本开始新增的balancer模块在PG分布优化方面效果非常明显,操作也非常简便,强烈推荐各位在集群上线之前进行这一操作,能够极大的提升整个集群...

3605
来自专栏张善友的专栏

Silverlight + Model-View-ViewModel (MVVM)

     早在2005年,John Gossman写了一篇关于Model-View-ViewModel模式的博文,这种模式被他所在的微软的项目组用来创建Expr...

3248
来自专栏陈仁松博客

ASP.NET Core 'Microsoft.Win32.Registry' 错误修复

今天在发布Asp.net Core应用到Azure的时候出现错误InvalidOperationException: Cannot find compilati...

5198
来自专栏张善友的专栏

Miguel de Icaza 细说 Mix 07大会上的Silverlight和DLR

Mono之父Miguel de Icaza 详细报道微软Mix 07大会上的Silverlight和DLR ,上面还谈到了Mono and Silverligh...

2997
来自专栏落花落雨不落叶

canvas画简单电路图

81111
来自专栏跟着阿笨一起玩NET

c#实现打印功能

3562
来自专栏我和未来有约会

Kit 3D 更新

Kit3D is a 3D graphics engine written for Microsoft Silverlight. Kit3D was inita...

2886

扫码关注云+社区