How to prevent app crash on SQLiteOpenHelper onUpgrade fail

129 views Asked by At

If there is an error during the onUpgrade method of my extended SQLiteOpenHelper class, the app crashes. I'd instead prefer to let the app continue opening the database and disable certain features that depend on the new schema.

I achieved it as follows, but feel this is a bit convoluted. Does anyone know a more supported or better way to let my app continue running on a failed database upgrade?

DBHelper class:

class DBHelper extends SQLiteOpenHelper {
    public int _actual_version = 0;

    public DBHelper(Context context) {
        super(context, "example_db_name", null, 2);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        this._actual_version = oldVersion;

        try {
            //do some upgrade code

            this._actual_version = newVersion;
        } catch (Throwable t) {
            //end the current transaction without committing
            db.endTransaction();
            //begin a new transaction to let the app continue
            //this also increments the db version
            db.beginTransaction();
        }
    }

    public int getActualVersion() { return this._actual_version; }
}

In my DB Adapter class:

class DBAdapter {
    private final DBHelper dbHelper;
    SQLiteDatabase dbW;

    public DBAdapter(Context context) {
        dbHelper = new DBHelper(context);
        dbW = dbHelper.getWritableDatabase();

        if (dbHelper.getActualVersion() != 2) {
            dbW.beginTransaction();
            dbW.setVersion(dbHelper.getActualVersion());
            dbW.setTransactionSuccessful();
            dbW.endTransaction();
        }
    }
}

Here is a stack trace of the app crash. I am simulating an error by using CREATE TBLE instead of CREATE TABLE in the query.

2021-09-13 09:00:00 7931-7931/com.example.android.myapp E/SQLiteLog: (1) near "TBLE": syntax error in "CREATE TBLE setting (id INTEGER PRIMARY KEY, setting TEXT NOT NULL)"
2021-09-13 09:00:00 7931-7931/com.example.android.myapp E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.android.myapp, PID: 7931
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.android.myapp/com.example.android.myapp.MainActivity}: android.database.sqlite.SQLiteException: near "TBLE": syntax error (code 1 SQLITE_ERROR): , while compiling: CREATE TBLE setting (id INTEGER PRIMARY KEY, setting TEXT NOT NULL)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
     Caused by: android.database.sqlite.SQLiteException: near "TBLE": syntax error (code 1 SQLITE_ERROR): , while compiling: CREATE TBLE setting (id INTEGER PRIMARY KEY, setting TEXT NOT NULL)
        at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
        at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:1045)
        at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:652)
        at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:590)
        at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:61)
        at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:33)
        at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1919)
        at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1841)
        at com.example.android.myapp.DBHelper.onUpgrade(DBHelper.java:159)
        at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:416)
        at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:340)
        at com.example.android.myapp.DBAdapter.<init>(DBAdapter.java:49)
        at com.example.android.myapp.MainActivity.onCreate(MainActivity.java:59)
        at android.app.Activity.performCreate(Activity.java:7994)
        at android.app.Activity.performCreate(Activity.java:7978)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.app.ActivityThread.main(ActivityThread.java:7656) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 
0

There are 0 answers