微信移动端数据库组件 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 条评论
登录 后参与评论

相关文章

来自专栏代码GG之家

google 分屏 横屏模式 按home键界面错乱故障分析(一)

你确定你了解分屏的整个流程? ? 之前分析文章列表: Android 关机对话框概率没有阴影故障分析 android recent key长按事件弹起触发最近列...

1788
来自专栏ITCloud的专栏

Neutron lbaas代理https实践

通过neutron-lbaas实现对https的代理,引用官方的解释https://docs.openstack.org/mitaka/networking-g...

44010
来自专栏小白课代表

限时免费 | 8月17日星期四限免应用

VOX: FLAC Music Player with MP3 & Equalizer[iOS][¥25→0]

643
来自专栏Seebug漏洞平台

TCTF/0CTF2018 XSS Writeup

刚刚4月过去的TCTF/0CTF2018一如既往的给了我们惊喜,其中最大的惊喜莫过于多道xss中Bypass CSP的题目,其中有很多应用于现代网站的防御思路。

6598
来自专栏大魏分享(微信公众号:david-share)

干货:什么叫一个好的Ansible Playbook?

前言: 本文是我和李尧老师一起实验。李尧是红帽高级培训讲师,目前负责红帽中国区员工内部技术培训与认证。 一、什么是一个好的Ansible Playbook? ?...

5779
来自专栏Seebug漏洞平台

TP-LINK WR941N路由器研究

作者:Hcamael@知道创宇404实验室 之前看到了一个CVE, CVE-2017-13772 是TP-Link WR940N后台的RCE, 手头上正好有一个...

2726
来自专栏jojo的技术小屋

转 前端代码异常日志收集与监控

☞ 收集日志的方法 平时收集日志的手段,可以归类为两个方面,一个是逻辑中的错误判断,为主动判断;一个是利用语言给我们提供的捷径,暴力式获取错误信息,如 try....

4249
来自专栏性能与架构

移动端是时候考虑抛弃jQuery了?

jQuery确实非常有用,它的初衷就是为诸多浏览器提供统一的接口,避免书写各种条件语句判断当前环境 移动端已经被类似 Safari 和 Chrome 的 web...

3485
来自专栏linux驱动个人学习

高通调试 SPI 屏的 bug

1462
来自专栏宏伦工作室

基于itchat实现微信群消息同步机器人1.0

3124

扫码关注云+社区