Reputation: 17339
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.
Upvotes: 4
Views: 466
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
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
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