这是第一次在简书上写文章,没想到写的就是纯技术类的文章,最近被这个小清新风格的网站吸引住了,也想换换风格,平时一般都是逛逛技术类的博客,但是发现简书这个网站,特别是被灌了一些鸡汤后,决定以后迁移到这个网站了,估计写的话也是一些关于技术类的文章把,谁让咱是一名不折不扣的攻城狮呢。
好的,闲话不多说,下面进入主题。
公司的一些产品中采用了 SQLCipher 这个库来来对数据库中的数据进行加密,但是在Android5.0版本上SQLCipher会出现readonly database错误:
net.sqlcipher.database.SQLiteException: attempt to write a readonly database
一直以为这是SQLCipher的问题,在Github上也有人提出过这个问题,但是这些方案没能解决我遇到的问题。
https://github.com/sqlcipher/android-database-sqlcipher/issues/161
这个问题只有在Android5.0上才会出现,但是目前采用5.0版本的机型应该不会很多(很多机型要么4.4要么就直接 >5.0)而且采用SQLCipher加密库的App也不是很多,所以这个问题出现的几率并不高,但是谁让我们的用户偏偏就用了Android5.0的机型呢,所以不得不去啃这块硬骨头(攻城狮嘛,攻不下城怎么能叫攻城狮呢)。
经过不懈的努力后,终于找到了解决方案:
Workaround for Nexus 9 SQLite file write operations on external dirs?
其实这并不是SQLCipher的问题,而是Android5.0的一个BUG,Google在Android的下一个版本中进行了修复,所以这个问题只有在Android5.0上才会出现。
国外大神也给出了非官方的解决方案:
https://android-review.googlesource.com/#/c/115351/
Store inodes in unsigned long long
In 32 bit ABIs, ino_t is a 32 bit type, while the st_ino field
in struct stat is 64 bits wide in both 32 and 64 bit processes.
This means that struct stat can expose inode numbers that are
truncated when stored in an ino_t.
The SDCard fuse daemon (/system/bin/sdcard) uses raw pointer
values as inode numbers, so on 64 bit devices, we're very likely
to observe inodes that need > 32 bits to represent.
The fileHasMoved function in sqlite compares the stored
inode value with a new one from stat, and when the stored
value is truncated, this check will falsely indicate that
the file has been moved. When the fileHasMoved function
triggers, other functions start returning errors indicating
that the database is in read-only mode.
NOTE: Bionic differs from glibc in that struct stat's st_ino
is *always* 64 bits wide, and not the same width as ino_t.
如果要修复该BUG的话,肯定得自己下载SQLCipher源码自己进行编译了,源码是C编写的,所以需要下载NDK才能进行编译。
NDK环境的搭建这里就不再叙述了,网上有很多的教程。
NDK环境搭建好之后,可以根据官方的教程进行编译:
https://www.zetetic.net/sqlcipher/sqlcipher-for-android/
当然在编译之前需要对源码进行修改,打开
https://android-review.googlesource.com/#/c/115351/3/dist/sqlite3.c
可以看到 sqlite.c 文件需要修改的地方:
25322 ino_t ino; /* Inode number */
修改为:
25322 #ifdef ANDROID
25323 // Bionic's struct stat has a 64 bit st_ino on both 32 and
25324 // 64 bit architectures. ino_t remains 32 bits wide on 32 bit
25325 // architectures and can lead to inode truncation.
25326 unsigned long long ino; /* Inode number */
25327 #else
25328 ino_t ino; /* Inode number */
25329 #endif
修改完成后保存文件,然后按照官方教程进行编译,编译完成后得到的so文件和jar文件即可用在Android5.0版本上。
----
给需要的朋友分享一个我自己编译的sqlcipher库http://pan.baidu.com/s/1i47SQSd