Erro do Sqlite no Android : error code 11: database disk image is malformed

Question:

I have a version of the Application in production, where it is allowed to bookmark a message, but this totally random error happens causing database corruption and resulting in application failure.

I know that I perform operations a little risky according to the people at SQLite.

The procedure is as follows:

  • starts the upgrade procedure
  • copies the user database to a temporary file
  • copy the new database to the device
  • attach the old base to the new
  • copy necessary data (such as favorite messages)
  • detach the temporary basis
  • excludes the temporary basis

This procedure is done on thousands of devices, most everything goes well, but it started to become frequent cases like this.

Does anyone know how to minimize this?

MISTAKE:

java.lang.RuntimeException: Unable to start activity ComponentInfo{br.com.redrails.torpedos/br.com.redrails.torpedos.MainActivity}: android.database.sqlite.SQLiteDatabaseCorruptException: error code 11: database disk image is malformed
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1970)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1995)
    at android.app.ActivityThread.access$600(ActivityThread.java:128)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1161)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4514)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: android.database.sqlite.SQLiteDatabaseCorruptException: error code 11: database disk image is malformed
    at android.database.sqlite.SQLiteStatement.native_1x1_long(Native Method)
    at android.database.sqlite.SQLiteStatement.simpleQueryForLong(SQLiteStatement.java:138)
    at android.database.DatabaseUtils.longForQuery(DatabaseUtils.java:791)
    at android.database.DatabaseUtils.longForQuery(DatabaseUtils.java:779)
    at br.com.redrails.torpedos.MensagemDAO.reloadQuantidadeTotal(MensagemDAO.java:144)
    at br.com.redrails.torpedos.MensagemDAO.getQuantidadeTotal(MensagemDAO.java:149)
    at br.com.redrails.torpedos.MainActivity.onCreate(MainActivity.java:85)
    at android.app.Activity.performCreate(Activity.java:4465)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1053)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1934)
    ... 11 more

Answer:

A few months ago, we started getting a lot of reports from users complaining about crashes in one of our apps. During investigation of the issue, it became clear that the error was due to some corruption in the SQLite file. I noticed that this problem also existed from a certain period until then. I also saw that this error occurred especially with newer devices that have more processing cores and higher clocks, and that in older devices that are less powerful and especially with only one core, this error occurred very little or simply did not occur. This made me conclude that the problem was related to competition. That here was linked to SQLite multi-thread access.

After some research and testing I saw the following points:

For each database there must be only one SqliteOpenHelper . As much as it sounds, there are no multiple database connections. A SqliteOpenHelper has only one connection. Ever. Even though the getWritableDatabase and getReadableDatabase methods make it look like there are two different connections, it just doesn't happen: it's the same connection.

This ensures that there are no collisions, concurrent accesses, to the database file.

In the case of the application in question, there were multiple SqliteOpenHelper and multiple Threads. The solution was a general persistence refactoring with the goal of having only one SqliteOpenHelper for each database (in this case there were three). There was thus a strong reduction in the occurrences of the problem, however there were still occurrences.

Finally, the database access methods were synchronized in order to avoid concurrent access to the SqliteOpenHelper . This is a measure that did not affect the application's performance, but which must be analyzed for each case.

Summing up:

  1. Keep only one SqliteOpenHelper per database
  2. If necessary and feasible, synchronize access
Scroll to Top