swalkner
swalkner

Reputation: 17339

getReadableDatabase often, but not always returns null

I see in the Crashlytics-Logs of my android application a NullPointerException in this code:

try {
    mSQLDBreader = this.getReadableDatabase();
} catch (SQLException e) {
    if (mSQLDBreader != null) {
        mSQLDBreader.close();
        mSQLDBreader = this.getReadableDatabase();
    }
}

mSQLDBreader... // NPE

As the previous developer isn't available any more, I don't know why it is tried two times, but the code seems to work sometimes, but often not. What can be reasons that this call returns null?

It seems like if this only happens on 2.3.x-devices, in my crashlogs all the affected devices are 2.3.5 and 2.3.6.

enter image description here

Upvotes: 4

Views: 466

Answers (3)

Pankaj kumar
Pankaj kumar

Reputation: 1377

open dataBase before use

 if (mDatabase != null && mDatabase.isOpen()) {
    return mDatabase;  // The database is already open for business
} else {
     return mDatabase.open();
}

Upvotes: 0

bigh_29
bigh_29

Reputation: 2633

Do you have any threading going on of your own creation? Some code that might be resetting mSQLDBreader via another method. Perhaps that thread occasionally runs and trashes the value of mSQLDBreader before you use it?

Have you ever witnessed this on an emulator running 2.3.x?

I looked at google's code for getReadableDatabase, and I don't see a way that it could return null. Gory details are below if you are interested. Based on what I see, I would suspect either a multithreading bug in your code, or a bug introduced by customizations to the android code by the manufacturer of the devices you tested (if that is even plausible.)

Gory details All paths through getReadableDatabase invoke methods on the return object after creating it. So the value can't be null at that point. Otherwise the NPE would be raised from inside.

Here is a snippet of the 2.3.6 code for getReadableDatabase. Actual source is available on grepcode.

public synchronized SQLiteDatabase getReadableDatabase() {
    if (mDatabase != null && mDatabase.isOpen()) {
        return mDatabase;  // The database is already open for business
    }

    if (mIsInitializing) { /* snip throw ISE */ }

    try {
        return getWritableDatabase();
    } catch (SQLiteException e) {
        // snip : throws or falls through below
    }

    SQLiteDatabase db = null;
    try {
        mIsInitializing = true;
        String path = mContext.getDatabasePath(mName).getPath();
        db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);

        // *** next line calls method on db. NPE would be here if db was null at this point. ***
        if (db.getVersion() != mNewVersion) {
            // snip throw. 
        }

        onOpen(db);
        Log.w(TAG, "Opened " + mName + " in read-only mode");
        mDatabase = db;
        return mDatabase;
    } finally {
        // snip : not relevant
    }
}

Notice that getReadableDatabase usually just returns the result of getWritableDatabase. He looks like this:

public synchronized SQLiteDatabase getWritableDatabase() {
    if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
        return mDatabase;  // The database is already open for business
    }

    if (mIsInitializing) {
        throw new IllegalStateException("getWritableDatabase called recursively");
    }

    // snip comment about locking
    boolean success = false;
    SQLiteDatabase db = null;
    if (mDatabase != null) mDatabase.lock();
    try {
        mIsInitializing = true;
        if (mName == null) {
            db = SQLiteDatabase.create(null);
        } else {
            db = mContext.openOrCreateDatabase(mName, 0, mFactory);
        }

        int version = db.getVersion();  // ** method called on result!
        // snip block that has more method calls and never nulls out db
        onOpen(db);
        success = true;
        return db;
    } finally {
        // snip

        mDatabase = db;

        // snip rest of finally block that isn't relevant.
    }
}

Lastly, it is important to note that both of these methods, as well as the close method of SqliteOpenHelper, are tagged with synchronize, so there is no way for one method to trash the state of the other if you have multiple threads calling these methods at the same time..

Upvotes: 1

Naveed
Naveed

Reputation: 3202

I have ran into similar problem in the past. It seems like keep closing and opening the database created this problem for me. If you are using the SQLLiteOpenHelper you should not be closing your database unless you have a really good reason to. See CommonsWare answer here which says

SQLiteOpenHelper holds onto the database you retrieve with getReadableDatabase()/getWritableDatabase(), and the point is for you to reuse that opened SQLiteDatabase object, particularly as you do work across multiple threads.

Upvotes: 0

Related Questions