9

I am using SQLiteAssetHelper library to deal with my App DB operations and it's working fine for almost every phone I've tested with. However some users with Android 2.3.3 - 2.3.7 report crashes when a call to open the db is made. I suspect this is due to some issue occuring the first time the db is being copied from the asset folder (such as insufficient space in the internal storage).

Helper class:

public class DbHelper extends SQLiteAssetHelper {

    final static String DB_NAME="db";
    static final int DB_VERSION = 1307181213;
    final Context context;

    public DbHelper(Context c) {
        super(c, DB_NAME, null, DB_VERSION);
        this.context = c;
        setForcedUpgradeVersion(DB_VERSION);
    }
}

Handler Class (I set a counter to timeout after 10s if db cannot be opened) :

public class DbHandlerDao {

    private SQLiteDatabase database;
private DbHelper dbHelper;

public DbHandlerDao(Context context) {
    dbHelper = new DbHelper(context);
}

public void open() throws SQLException {
    boolean opened = false;
    int timeout = 0;
    while (!opened && timeout < 10000) {
        try {
            database = dbHelper.getReadableDatabase();
            opened = true; 
        }
        catch (IllegalStateException e) {
            timeout += 500;
            try {
                Thread.sleep(500);
            } catch (Exception e2) {

            }
        }
    }
}

public void close() {
    dbHelper.close();
}

Calling class:

private class DbAsyncTask extends AsyncTask<Long, Void, Void> {

        //...

        @Override
        protected Void doInBackground(Long... params) {
            dataHandler.open();

Exception:

java.lang.RuntimeException: An error occured while executing doInBackground()
    at android.os.AsyncTask$3.done(AsyncTask.java:200)
    at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)
    at java.util.concurrent.FutureTask.setException(FutureTask.java:125)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
    at java.lang.Thread.run(Thread.java:1019)
Caused by: java.lang.NullPointerException
    at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getWritableDatabase(SQLiteAssetHelper.java:180)
    at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getReadableDatabase(SQLiteAssetHelper.java:257)
    at com.example.DbHandlerDao.open(DbHandlerDao.java:25)
    at com.example.SearchActivity$DbAsyncTask.doInBackground(SearchActivity.java:244)
    at com.example.SearchActivity$DbAsyncTask.doInBackground(SearchActivity.java:1)
    at android.os.AsyncTask$2.call(AsyncTask.java:185)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
anddev84
  • 1,473
  • 12
  • 30
Amine Kadimi
  • 116
  • 1
  • 4
  • I'm experiencing what appears to be the same bug on several devices running Android 4.3 and 4.4. The odd thing is that I can't consistently reproduce it. – Ben Jan 11 '14 at 03:15
  • I'd post it as an issue on the github project itself, since it's almost guaranteed to be an issue with the library - standard SQLiteOpenHelper doesn't have this issue. Seems other people have also had this problem: https://github.com/jgilfelt/android-sqlite-asset-helper/issues/23 – anddev84 Jan 11 '14 at 05:51
  • 3
    Thanks, good idea. But after some more investigagion, I've realized that this was my own mistake. To make a long story short, I was copying a database in an `AsyncTask` called from the `onCreate` method of an activity that then tried to populate a `ListView` using that same database. Turns out that that only worked when I got lucky and the background task finished before the `ListView` needed the database! – Ben Jan 11 '14 at 21:44
  • Just a quick note no loops based on a timer that have no means for a user to cancel. You might end up locking the ui thread and your app will appear unresponsive. – danny117 Jan 16 '14 at 22:08
  • danny117, I am calling dataHandler.open() in an asyncTask which runs in background – Amine Kadimi Jan 17 '14 at 21:16
  • facing the same issue on a production app on a select devices . Any concrete solution for this. Even any concrete reason why this happens ? – thedarkpassenger Feb 02 '16 at 06:53

2 Answers2

0

Possibly the users have cleared their storage or they don't have a SD CARD?

Probably whats going on for sporadic crashes.

** Have you implemented the oncreate and onupgrade methods?

I use the open helper. So I never have trouble opening the database.

 public class RidesDatabaseHandler extends SQLiteOpenHelper {

...

    // Creating Tables
@Override
public void onCreate(SQLiteDatabase db) {
    String CREATE_CONTACTS_TABLE = "CREATE TABLE " + TABLE_RIDES + "("
            + KEY_ID + " INTEGER PRIMARY KEY," + KEY_NAME + " TEXT,"
            + KEY_DESCRIPTION + " TEXT," + KEY_BOUNDS_NELAT + " TEXT,"
            + KEY_BOUNDS_NELONG + " TEXT," + KEY_BOUNDS_SWLAT + " TEXT,"
            + KEY_BOUNDS_SWLONG + " TEXT," + KEY_RESRAWID + " TEXT,"
            + KEY_RESDRAWABLEID + " TEXT," + KEY_PATH + " TEXT,"
            + " CONSTRAINT UNQ_" + KEY_PATH + " UNIQUE (" + KEY_PATH
            + ") ON CONFLICT ABORT)";

    // VERSION 43 ADDS THE KEY_DATE_UPDATE COLUMN
    CREATE_CONTACTS_TABLE += " , " + KEY_DATE_UPDATE + " INTEGER DEFAULT 0";

    db.execSQL(CREATE_CONTACTS_TABLE);
}

// Upgrading database
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    int upgradeTo = oldVersion + 1;
    while (upgradeTo <= newVersion) {
        switch (upgradeTo) {
        // update old resrawid's to constants
        // this was caused when I saved R values in the database
        // and then later realized they would change.
        // the old resrawid is changed to a constant.
        case 42:
            ContentValues values;
            @SuppressWarnings("unused")
            // used for debugging so I can see the value of the update.
            int res;
            int rs;
            rs = 2130968576;
            values = new ContentValues();
            values.put(
                    KEY_RESRAWID,
                    com.gosylvester.bestrides.ImageTextListViewActivity.SAMPLE_DRAGON);
            res = db.update(TABLE_RIDES, values, KEY_RESRAWID + " = ?",
                    new String[] { String.valueOf(rs) });

            rs = 2130968577;
            values = new ContentValues();
            values.put(
                    KEY_RESRAWID,
                    com.gosylvester.bestrides.ImageTextListViewActivity.SAMPLE_M119);
            res = db.update(TABLE_RIDES, values, KEY_RESRAWID + " = ?",
                    new String[] { String.valueOf(rs) });

            rs = 2130968578;
            values = new ContentValues();
            values.put(
                    KEY_RESRAWID,
                    com.gosylvester.bestrides.ImageTextListViewActivity.SAMPLE_MEDINA);
            res = db.update(TABLE_RIDES, values, KEY_RESRAWID + " = ?",
                    new String[] { String.valueOf(rs) });
            break;
        case 43:
            // new column added for last_viewed.
            db.execSQL(VERSION_43_ALTER);
            break;

        case 44:
            // new index on KEY_DATE_UPDATE DESC
            db.execSQL(VERSION_44_CREATE_INDEX);
        }
        upgradeTo++;
    }

Good Luck

danny117
  • 5,581
  • 1
  • 26
  • 35
0

This question didn't have an accepted answer even though the OP indicated that it was solved in the comments. So here is the answer:

Don't try to copy the database with an AsyncTask that is called from a method (such as onCreate) and then try to populate a ListView with data from the database in the same method. If the AsyncTask has not finished by the time the ListView needs the data then a NullPointerException will be raised.

Instead, populate the ListView from the onPostExecute method of the AsyncTask. That will ensure that the database has finished copying.

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • I am also facing this issue on some devices. I have tried to call the database for once at the splash screen but it cashes on some devices. Could you please eleborate the onPostExecute implementation for this. Cause I think this may be the permanent solution. – Ankit Sahu Aug 08 '22 at 10:45
  • @AnkitSahu, see https://stackoverflow.com/a/25647882/3681880 – Suragch Aug 10 '22 at 19:41