0

I use a SQLite database in my Android app which I access using a DBHelper and a custom ContentProvider. I now want to import a database by replacing the current one with one I previously exported from the app by simply copying it to another location.

However, after replacing the database, the in-memory copy of the old database continues to be used until I close and restart the app.

-> How can I force a reload of the database?

Additional Info

It works well, when I export the .db file along with the .db-shm and .db-wal files and import all 3 files instead of just the .db file.

However, if I open the .db file with sqlite3 on my laptop, close it (such that the .db file contains all the necessary information and .db-shm and .db-wal are automatically deleted) and try to import just this .db file, the behavior is as described above.

My Code

The DBHelper class:

public class MyDbHelper extends SQLiteOpenHelper {

public static final String DATABASE_NAME = "mydb.db";
private static final int DATABASE_VERSION = 2;
private static MyDbHelper mInstance = null;

MyDbHelper(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL("CREATE TABLE table1 ...;");
    db.execSQL("CREATE TABLE table2 ...;");
}

@Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
    db.execSQL("CREATE TABLE IF NOT EXISTS table3 ...;");
}

static MyDbHelper getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new MyDbHelper(context.getApplicationContext());
    }
    return mInstance;
}}

In the ContentProvider onCreate() method, I instantiate the DBHelper like this:

mDbHelper = MyDbHelper.getInstance(getContext());

and then get the writable/readable database instance inside of each method in the ContentProvider as suggested here.

What I tried already

I tried implementing a reopen method in the DBHelper

public static void reopen(Context context) {
        mDBHelper = new MyDBHelper(context);
    }

as suggested here and calling it after the import, but still the new database is only used once the app was closed and restarted.

I also tried a reset method

public void reset(Context context) {
    mDBHelper.close();
    mDBHelper = new MyDBHelper(context);
}

as described here but this results in the app crashing with

2019-08-03 14:58:13.091 21814-21887/? E/SQLiteLog: (522) statement aborts at 3: [SELECT _id, title, cover_path FROM albums ORDER BY CAST(title as SIGNED) ASC, LOWER(title) ASC] disk I/O error
2019-08-03 14:58:13.092 21814-21887/? E/SQLiteQuery: exception: disk I/O error (code 522 SQLITE_IOERR_SHORT_READ); query: SELECT _id, title, cover_path FROM albums ORDER BY CAST(title as SIGNED) ASC, LOWER(title) ASC
2019-08-03 14:58:13.115 21814-21887/? E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #4
    Process: com.mycompany.myapp, PID: 21814
    java.lang.RuntimeException: An error occurred while executing doInBackground()
        at android.os.AsyncTask$3.done(AsyncTask.java:354)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
        at java.util.concurrent.FutureTask.run(FutureTask.java:271)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: android.database.sqlite.SQLiteDiskIOException: disk I/O error (code 522 SQLITE_IOERR_SHORT_READ)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForCursorWindow(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForCursorWindow(SQLiteConnection.java:859)
        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:149)
        at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:137)
        at android.content.ContentResolver.query(ContentResolver.java:821)
        at android.content.ContentResolver.query(ContentResolver.java:752)
        at android.content.CursorLoader.loadInBackground(CursorLoader.java:68)
        at android.content.CursorLoader.loadInBackground(CursorLoader.java:45)
        at android.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:319)
        at android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:73)
        at android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:61)
        at android.os.AsyncTask$2.call(AsyncTask.java:333)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:764) 
flackbash
  • 488
  • 2
  • 16
  • Why do you have a `ContentProvider`? Very few Android apps need to be implementing a database-backed `ContentProvider`. Regardless, before attempting to restore your database, you need to close all open connections to the existing database, and that will include the connection held by your `ContentProvider`. – CommonsWare Aug 03 '19 at 13:15
  • @CommonsWare isn't that what I'm doing when calling `mDBHelper.close()`? How else should I close it? – flackbash Aug 03 '19 at 13:22
  • "isn't that what I'm doing when calling mDBHelper.close()?" -- I do not know where that method is and whether that is the helper being used by the `ContentProvider`. Also, you do not appear to be closing the helper before restoring the database. You seem to be doing it after restoring the database. That is very risky -- what if something tries manipulating the database while you are in the middle of restoring it? – CommonsWare Aug 03 '19 at 13:25
  • Whether I call `mDBHelper.close()` before or after I change the db on disk - I always get the `SQLiteDiskIOException` error posted above. And yes, `mDBHelper` is the DBHelper being used by my custom `ContentProvider`. – flackbash Aug 03 '19 at 13:34

0 Answers0