If you’ve used Android’s Notepad tutorial (Version 3) , or modeled your own Activity on Notepadv3, then you’ve probably seen log messages that look something like this:
android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here at android.database.sqlite.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:62) at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:80) at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:36) at android.database.sqlite.SQLiteDatabase.compileStatement(SQLiteDatabase.java:1145) at android.database.sqlite.SQLiteDatabase.updateWithOnConflict(SQLiteDatabase.java:1671) at android.database.sqlite.SQLiteDatabase.update(SQLiteDatabase.java:1622) at com.android.demo.notepad3.NotesDbAdapter.updateNote(NotesDbAdapter.java:186) at com.android.demo.notepad3.NoteEdit.saveState(NoteEdit.java:106) at com.android.demo.notepad3.NoteEdit.onPause(NoteEdit.java:87) at android.app.Activity.performPause(Activity.java:3842) at android.app.Instrumentation.callActivityOnPause(Instrumentation.java:1190) at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3335) at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3305) at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:3288) at android.app.ActivityThread.access$2500(ActivityThread.java:125) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2044) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:123) at android.app.ActivityThread.main(ActivityThread.java:4627) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:521) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626) at dalvik.system.NativeStart.main(Native Method)
This is resolved easily enough by overriding the onDestroy
method in each of the Activities in your application — or at least, each Activity that uses the database. In the Notepadv3 app, this means overriding onDestroy
in both the Notepadv3 and NoteEdit classes. The same definition is suitable for both classes:
public void onDestroy() { super.onDestroy(); // Replace mDbHelper as needed with your database connection, or // whatever wraps your database connection. (See below.) mDbHelper.close(); }
In the Notepadv3 tutorial, the mDbHelper
field is an instance of the class NotesDbAdapter
, which wraps the database connection. NotesDbAdapter
comes from google with the close
method already defined, but there’s no code that calls it. With this change, we’re supplying the code that calls close
at the right time. (I think.)
In general: If your activity opens a database by calling either SQLiteOpenHelper.getReadableDatabase
or SQLiteOpenHelper.getWritableDatabase
, then you should rely on SQLiteOpenHelper.close
to close that database. Your activity’s onDestroy
method must result in a call to the close
method for any instances of SQLiteOpenHelper
, or classes derived from it, that you’ve created. (In the code above, mDbHelper.close
will have that effect.) SQLiteOpenHelper.close
will then close any database handle that it created for you.