场景
Jni下编译SQLite源码作为数据库,在测试手机:型号(Redmi Note 2) Android版本(5.0.2 LRX22G)系统下使用,尝试写数据库的时候,返回错误信息:attempt to write a readonly database
解决
在sqlite.c文件中查找
ino_t ino; /* Inode number */
修改为
unsigned long long ino; /* Inode number */
错误说明
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.
参考
http://www.jianshu.com/p/30139ef31230
解决过程:
1 怀疑是读写权限的问题,但是其他文件也有读写,明显不成立,并且已经提供了读写的权限:
<uses-permission android:name="android.permission.INTERNET" />
2 怀疑是使用MTP模式共享机身存储到电脑,导致两边同时编辑,通过关闭MTP媒体共享,不成立
Redmi Note 2手机请勿关闭MTP媒体设备,否则需要恢复出厂设置才能够重新共享到电脑。
3 拷贝test.db和test.db-journal文件到Windows系统,并且使用SQLite控制台进行修改,没有任何的
问题。当前使用的是相同的SQLite源码编译的控制台程序。进行数据的插入过程中,会自动删除journal归档文件,并没有提示只读。另外单独拷贝test.db,直接操作,也没有任何的问题。
4 尝试关闭Java层对数据库的读写访问,只是允许NDK层直接操作数据库,防止多线程访问数据库,还是出现同样的结果,当然不知道是否是sqlite3_open与sqlite3_open_v2的接口是否会产生不同的结果,目前采用第一种方法访问数据库。
5 尝试获取Android系统中SQLite的版本,调用getVersion返回1,没有实质上的帮助。
6 是否是其他的Java层获取数据库的连接,没有关闭导致问题的出现?SQliteOpenHelper内部只缓存一个数据库的连接,在多线程的使用过程中,不要频繁的调用close,而应该保存一个唯一的一个访问实例,但是对于多进程之间的访问,也带来问题http://blog.sina.com.cn/s/blog_5de73d0b0102w0g0.html
7 虽然NDK无法修改数据,但是可以读取数据,而Java层却可以轻松的完成数据库的修改操作。
8 尝试调用beginTransaction和endTransaction对数据库的操作进行事务加锁还是没有任何的效果
9 怀疑是Android目录的读取问题,NDK层的SQLite读取数据库文件,发现有问题,但是没有办法找到journal归档文件,因此认为是只读,也是合理的
总结:
经过上述种种的研究分析,得到如下的猜测:NDK层的SQLite实例在系统开机启动之后,会检查是否存在journal档案。如果是通过程序自动删除该journal档案。但是如果程序无法自动删除,目前怀疑就是因为版本之间的不对称,导致无法删除journal,SQLite数据库因此数据库变成只读的状态。
折中方案:
NDK层只是负责只读数据,如果有任何的修改,都需要调用Java的接口进行修改数据,虽然有SQLite的源码,但是NDK层不好调试。
本文转自fengyuzaitu 51CTO博客,原文链接:http://blog.51cto.com/fengyuzaitu/1946701,如需转载请自行联系原作者