I had some similar issues with Android SQLite databases. I submitted a bug report on it long ago at https://code.google.com/p/android/issues/detail?id=174566. This report discusses my findings on the reasons in more detail. I am not sure if it is related to your issue or not, but it seems to share some characteristics.
To summarize here, my debugging indicated that Android opens the database file, calls onUpgrade(), and if you replace the database file during the onUpgrade() call, the Android side file handle points to the old file and thus causes the app to crash when you return from onUpgrade() and Android tries to access the old file.
Here is some code I used to get around the issue:
When the app starts, I did this in onCreate():
Thread t = new Thread(new Runnable() {
@Override
public void run() {
Context context = getApplicationContext();
DBReader.copyDB(MainActivity.this);
DBReader.initialize(context);
}
});
t.start();
This causes the update of the database file to happen in the background while the app is starting and user is occupied with awe of the awesome application. Because my file was rather big and it took a while to copy. Notice that I completely avoid doing anything in onUpgrade() here.
DBReader is my own class, for which the main code of interest is this:
SharedPreferences prefs = context.getSharedPreferences(Const.KEY_PREFERENCES, Context.MODE_PRIVATE);
//here we have stored the latest version of DB copied
String dbVersion = prefs.getString(Const.KEY_DB_VERSION, "0");
int dbv = Integer.parseInt(dbVersion);
if (checkIfInitialized(context) && dbv == DBHelper.DB_VERSION) {
return;
}
File target = context.getDatabasePath(DBHelper.DB_NAME);
String path = target.getAbsolutePath();
//Log.d("Awesome APP", "Copying database to " + path);
path = path.substring(0, path.lastIndexOf("/"));
File targetDir = new File(path);
targetDir.mkdirs();
//Copy the database from assets
InputStream mInput = context.getAssets().open(DBHelper.DB_NAME);
OutputStream mOutput = new FileOutputStream(target.getAbsolutePath());
byte[] mBuffer = new byte[1024];
int mLength;
while ((mLength = mInput.read(mBuffer)) > 0) {
mOutput.write(mBuffer, 0, mLength);
}
mOutput.flush();
mOutput.close();
mInput.close();
SharedPreferences.Editor edit = prefs.edit();
edit.putString(Const.KEY_DB_VERSION, "" + DBHelper.DB_VERSION);
edit.apply();
and the code for checkIfInitialized():
public static synchronized boolean checkIfInitialized(Context context) {
File dbFile = context.getDatabasePath(DBHelper.DB_NAME);
return dbFile.exists();
}
So, to make the story short, I just avoided onUpgrade() alltogether and implemented my own custom upgrade functionality. This avoids the problem of the Android OS crashing on old and invalid filehandles caused by change of the database in onUpgrade().
Kind of odd, I though, for the onUpgrade() to cause the OS to crash your app if you actually end up upgrading your database file in a function intended to let you upgrade your database. And the Google comments on the bug report were made few years after so I no longer had the original crashing code around for easy proof of concept.
Your problem might be slightly different in that you are not copying the database file, but you still seem to be modifying it, so the root cause might be similar.