Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLiteDatabase close() function causing NullPointerException when multiple threads

I discovered in my project that the close() function in the SQLiteDatabase implementation on Android throws a NullPointerException when running multiple threads that open the database, insert data, and then close the database. Everything runs smoothly unless I allow each thread to close() after inserting into the database.

Here is an example of what one of the writing threads might look like

        ContentValues values = new ContentValues();
        values.put(CallDetailsStorageConstants.COLUMN_NAME_REMOTE_NAME_IDX, (String) "");
        values.put(CallDetailsStorageConstants.COLUMN_NAME_REMOTE_MEETINGID_IDX, (String) "");

        CalculonDatabase callDetailsOpenHelper = CalculonDatabase.getInstance(mContext);
        SQLiteDatabase dbw = callDetailsOpenHelper.getWritableDatabase();

        long rowid = 0;
        try {
            rowid = dbw.insertWithOnConflict(CallDetailsTable.CALL_DETAILS_TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE);
        } catch (SQLiteConstraintException e) {
            e.printStackTrace();
        } finally {
            dbw.close();
        }

You can see that at the end a call to close() is made. When running multiple threads I get this exception.

11-03 03:39:26.128: E/AndroidRuntime(977): FATAL EXCEPTION: Thread-17
11-03 03:39:26.128: E/AndroidRuntime(977): java.lang.NullPointerException
11-03 03:39:26.128: E/AndroidRuntime(977):  at     android.database.sqlite.SQLiteStatement.releaseAndUnlock(SQLiteStatement.java:283)
11-03 03:39:26.128: E/AndroidRuntime(977):  at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:96)
11-03 03:39:26.128: E/AndroidRuntime(977):  at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1933)
11-03 03:39:26.128: E/AndroidRuntime(977):  at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1864)
11-03 03:39:26.128: E/AndroidRuntime(977):  at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:636)
11-03 03:39:26.128: E/AndroidRuntime(977):  at android.database.sqlite.SQLiteDatabase.beginTransactionNonExclusive(SQLiteDatabase.java:551)
11-03 03:39:26.128: E/AndroidRuntime(977):  at android.database.sqlite.SQLiteStatement.acquireAndLock(SQLiteStatement.java:240)
11-03 03:39:26.128: E/AndroidRuntime(977):  at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:111)
11-03 03:39:26.128: E/AndroidRuntime(977):  at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1737)
11-03 03:39:26.128: E/AndroidRuntime(977):  at com.smash.DBWritingThread.run(DBWritingThread.java:50)

My question then is why does this error happen only when using the close() function? Also, is it ok to use close() at a much later time or possibly never?

Any tips with this issue are very much appreciated.

like image 445
user1028181 Avatar asked Nov 03 '11 17:11

user1028181


2 Answers

I have your answers!

Never close the db. It is that simple.

Make sure you only have one, repeat, *one Helper instance per application. Share it with all threads.

http://www.touchlab.co/blog/single-sqlite-connection/

Here's some more info on the internals of sqlite and locking:

http://www.touchlab.co/blog/android-sqlite-locking/

like image 163
Kevin Galligan Avatar answered Nov 13 '22 03:11

Kevin Galligan


You don't need to bother. See this discussion which includes confirmation that closing the database is done by the system as needed if/when the process is killed off.

The relevant quote, from a Google employee, is:

A content provider is created when its hosting process is created, and remains around for as long as the process does, so there is no need to close the database -- it will get closed as part of the kernel cleaning up the process's resources when the process is killed.

like image 28
Graham Borland Avatar answered Nov 13 '22 01:11

Graham Borland