试图写一个只读数据库......但我不是

Attempt to write a readonly database... but I'm not

我有只读数据库连接。有时,当使用 SELECT 查询从数据库中读取数据时,它会抛出 SQLiteReadOnlyDatabaseException.

我这样打开连接:

return SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);

查询是:

Select * FROM BudgetVersions WHERE entityId = ?

我使用db.rawQuery()从数据库中读取数据,像这样:

String query = ...;
Cursor c = db.rawQuery(query, new String[]{ activeBudgetId });
try {
    if (c.moveToFirst()) {            
        bv.versionName = c.getString(c.getColumnIndexOrThrow("versionName"));
        return bv;
    } else {
        return null;
    }
} finally {
    c.close();
}

很少,我在调用 c.moveToFirst() 时遇到这样的崩溃:

Caused by: android.database.sqlite.SQLiteReadOnlyDatabaseException: attempt to write a readonly database (code 776)
at android.database.sqlite.SQLiteConnection.nativeExecuteForCursorWindow(Native Method)
at android.database.sqlite.SQLiteConnection.executeForCursorWindow(SQLiteConnection.java:845)
at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:836)
at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62)
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:144)
at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:197)
at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:237)

作为解决方法,我可以尝试使用 writable 数据库连接,但我想知道为什么会发生崩溃。

我正在阅读的 table 是一个标准的 SQLite table:

CREATE TABLE BudgetVersions (
    entityId        VARCHAR  PRIMARY KEY NOT NULL UNIQUE,
    budgetId        VARCHAR  NOT NULL,
    versionName     VARCHAR  NOT NULL,
    dateFormat      VARCHAR,
    currencyFormat  VARCHAR,
    lastAccessedOn  DATETIME,
    isTombstone     BOOL     NOT NULL,
    deviceKnowledge NUMERIC  NOT NULL
);

我已经看到崩溃发生在 KitKat 模拟器和设备 运行 Lollipop 上。


有一个单独 可写连接打开到同一个数据库同时,由WebView 拥有。数据库由 WebView 中的 Java 脚本代码更新,并使用此只读连接从本机 Android/Java 层读取。

我认为这可能是问题的最终原因,但我想详细了解为什么只读连接会干扰单独的可写连接。

我很清楚一般建议是使用单一连接到数据库,但由于可写连接由 WebView 所有,我无法从 Java代码。

通过将其更改为可写数据库连接来解决。线索在 documentation for the 776 error code:

(776) SQLITE_READONLY_ROLLBACK

The SQLITE_READONLY_ROLLBACK error code is an extended error code for SQLITE_READONLY. The SQLITE_READONLY_ROLLBACK error code indicates that a database cannot be opened because it has a hot journal that needs to be rolled back but cannot because the database is readonly.

在开发过程中,我经常中断当前 运行ning 应用程序来安装和 运行 新版本。这会导致 currently-运行ning 应用程序被系统强制停止。如果 WebView 中的 Java 脚本代码在应用程序被破坏时正在通过其单独的可写连接写入数据库,那么将留下 hot journal

新版应用启动时,原生Java代码中的只读数据库连接被打开。当此连接发现日志时,它会尝试回滚日志。因为它是只读连接,所以失败了。

(这与我进行更改后在启动时立即观察到的崩溃相吻合。)

因此,正确的解决方法是使 Java 连接成为可写连接。此连接在正常操作期间从不尝试写入,但在通过 WebView 的可写连接从先前中断的写入中恢复时必须写入。