2015-05-06 1 views
26

Ich habe eine schreibgeschützte Datenbankverbindung. Beim Lesen von Daten aus der Datenbank mit einer Abfrage SELECT wird manchmal ein SQLiteReadOnlyDatabaseException ausgelöst.Versuch, eine Readonly-Datenbank zu schreiben ... aber ich bin nicht

Ich öffne die Verbindung wie folgt aus:

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

Die Abfrage ist:

Select * FROM BudgetVersions WHERE entityId = ? 

Ich lese Daten aus der Datenbank db.rawQuery() verwendet wird, wie folgt aus:

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(); 
} 

Sehr selten , Bekomme ich einen solchen Crash, innerhalb des Anrufs 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) 

Als Workaround könnte ich stattdessen eine beschreibbare Datenbankverbindung versuchen, aber ich würde gerne wissen, warum der Absturz passiert.

Die Tabelle aus lese ich eine Standard-SQLite Tabelle:

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 
); 

ich den Absturz sowohl auf ein KitKat-Emulator und ein Gerät passieren gesehen habe laufen Lollipop.


Es gibt eine separate beschreibbare Verbindung offen zu derselben Datenbank zugleich von einem WebView gehört. Die Datenbank wird durch JavaScript-Code im WebView aktualisiert und in der nativen Android/Java-Schicht mit dieser schreibgeschützten Verbindung gelesen.

Ich erwarte, dass dies die ultimative Ursache des Problems sein kann, aber ich würde gerne im Detail verstehen, warum eine schreibgeschützte Verbindung eine separate beschreibbare Verbindung stören würde.

Ich bin mir bewusst, dass der allgemeine Ratschlag ist, eine einzelne Verbindung mit der Datenbank zu verwenden, aber da die beschreibbare Verbindung im Besitz der WebView ist, habe ich keinen einfachen Zugriff darauf aus dem Java-Code.

+0

Beachten Sie, dass 'cwac-loaderex' seit geraumer Zeit nicht mehr verwendet wird. Ist das ein Crash, den Sie während der Entwicklung oder nur in der Produktion sehen? Beachten Sie, dass 'getCount()' tatsächlich die SQL-Abfrage ausführt, die Sie dem Loader zum Laden gegeben haben, da 'rawQuery()'/'query()' einen 'Cursor' zurückgibt, aber die Abfrage nicht sofort ausführt. – CommonsWare

+0

Die Erwähnung von Loadern und CWAC-LoaderEx wurde entfernt. Das Problem tritt auch ohne sie auf. Den Absturz beobachtet man während der Entwicklung. –

+0

Ich würde sagen, es ist ein Fehler in wie SQLlite eine Datenbankinstanz basierend auf den Flags zurückgibt, möglicherweise im Zusammenhang mit Multithreading. Ich würde annehmen, zu versuchen, eine Singleton-Instanz der schreibbaren Datenbank für alle Ihre Operationen zu halten sollte es lösen (auch alle Ihre Verbindungen in Lese-Schreib-öffnen) – njzk2

Antwort

15

Gelöst durch Ändern in eine beschreibbare Datenbankverbindung. Der Hinweis war im documentation for the 776 error code:

(776) SQLITE_READONLY_ROLLBACK

Der SQLITE_READONLY_ROLLBACK Fehlercode ist ein erweiterter Fehlercode für SQLITE_READONLY. Der SQLITE_READONLY_ROLLBACK-Fehlercode gibt an, dass eine Datenbank nicht geöffnet werden kann, da ein Hot-Journal vorliegt, das zurückgesetzt werden muss, aber nicht, da die Datenbank schreibgeschützt ist.

Während der Entwicklung unterbricht ich häufig die aktuell laufende App, um eine neue Version zu installieren und auszuführen. Dadurch wird die aktuell ausgeführte App vom System zwangsweise gestoppt. Wenn der Javascript-Code in der WebView gerade dabei ist, über die separate, beschreibbare Verbindung in die Datenbank zu schreiben, wenn die App mit einem Atom-Kernwaffenbefehl versehen ist, wird ein hot journal zurückgelassen.

Wenn die neue Version der App gestartet wird, wird die schreibgeschützte Datenbankverbindung im systemeigenen Java-Code geöffnet. Wenn diese Verbindung das Journal entdeckt, wird versucht, das Journal zurückzusetzen. Und weil es eine schreibgeschützte Verbindung ist, schlägt es fehl.

(Das paßt mit dem Absturz sofort beim Start beobachtet wird, nachdem ich eine Änderung vorgenommen habe.)

Die richtige fix damit die Java-Verbindung eine beschreibbare Verbindung herzustellen ist. Diese Verbindung versucht nie während des normalen Betriebs ein Schreiben, aber sie muss schreiben, wenn sie von einem vorherigen unterbrochenen Schreiben durch die beschreibbare Verbindung des WebViews wiederherstellt.

+4

Hallo Graham, können Sie mir dabei helfen, die Java-Verbindung zu einem beschreibbaren machen? Danke – user219882

+0

Rückgabe SQLiteDatabase.OpenDatabase (Pfad, null, SQLiteDatabase.OPEN_READWRITE); – ketom