標籤:android ndk jni 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層不好調試。
Android5.0上運行SQLite資料庫出現attempt to write a readonly database的解決方案