天天看點

Android5.0上運作SQLite資料庫出現attempt to write a readonly database的解決方案

場景

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,如需轉載請自行聯系原作者