I have an existing database based on SQLiteOpenHelper
that has several versions and code to upgrade it and that works fine. But in case a user installs an older version of the app (that expects a lower database version) it will currently crash - the ContentProvider
using it can't access the database. I'd like to prevent it from crashing but I don't want to actually downgrade the database - adding the code to do that would be pain. Dropping all tables would certainly work but starting with a fresh file is imo cleaner and less error prone.
That's about what the database helper looks like - nothing special
public class MyDbHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 3;
private static final String DATABASE_NAME = "my.db";
public MyDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
onUpgrade(db, 0, DATABASE_VERSION);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion < 1) db.execSQL("CREATE TABLE A...");
if (newVersion < 2) db.execSQL("CREATE TABLE B...");
if (newVersion < 3) db.execSQL("CREATE TABLE C...");
}
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// I'd like to delete the database here
// the only problem is that I can't from here
// since this is called in the middle of getWritableDatabase()
// and SQLiteDatabase has no .recreate() method.
}
}
The possible ways I've come up to do that are:
ContentProvider
, delete the file and request to open the database again. - I don't like that since it's not the responsibility of the provider.SQLiteOpenHelper
with my own copy of that class that deletes the file instead of calling onDowngrade
- Problem is that it's using package private parts of SQLiteDatabase
(e.g. .lock()
) which I can't replace without duplicating SQLiteDatabase
too (that would probably result in duplicating the whole sqlite stack).Is there any good approach to do that or do I have to go the DROP TABLES
way e.g. like described here?
I've figured out a way that works nicely by extending SQLiteOpenHelper and all I need to do in MyDbHelper
is to extend this class.
public abstract class DeletingSQLiteOpenHelper extends SQLiteOpenHelper {
private static final String TAG = DeletingSQLiteOpenHelper.class.getSimpleName();
private final File mDatabaseFile;
public DeletingSQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
DatabaseErrorHandler errorHandler) {
super(context, name, factory, version, errorHandler);
mDatabaseFile = context.getDatabasePath(name);
}
public DeletingSQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
mDatabaseFile = context.getDatabasePath(name);
}
@Override
public synchronized SQLiteDatabase getWritableDatabase() {
try {
return super.getWritableDatabase();
} catch (SQLiteDowngradeFailedException e) {
// that's our notification
}
// try to delete the file
mDatabaseFile.delete()
// now return a freshly created database
return super.getWritableDatabase();
}
@Override
public final void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// throwing a custom Exception to catch it in getWritableDatabase
throw new SQLiteDowngradeFailedException();
}
// that's the exception
static class SQLiteDowngradeFailedException extends SQLiteException {
public SQLiteDowngradeFailedException() {}
public SQLiteDowngradeFailedException(String error) {
super(error);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With