Android database cursor lifetime
I have been trying to understand and solve this problem for the last three days. I have multiple ListActivities where each one queries the same database. In every activity I close and reopen DB connection in onPause() and onResume() respectively. I populate list adapters with cursors which I also close and reopen:
@Override
public void onPause(){
super.onPause();
if(currentCursor != null){
currentCursor.close();
currentCursor = null;
}
if(mDbHelper != null){
mDbHelper.close();
mDbHelper = null;
}
}
@Override
public void onResume(){
super.onResume();
if(mDbHelper == null){
mDbHelper = new DbHelper(this);
mDbHelper.open();
}
if(currentCursor == null){
new LoadNewListTask().execute();
}
}
private class LoadNewListTask extends AsyncTask<String, Void, String> {
ProgressDialog dialog;
@Override
开发者_开发百科 protected void onPreExecute() {
dialog = new ProgressDialog(context);
dialog.setMessage("Loading...");
dialog.show();
}
@Override
protected String doInBackground(String... arg0) {
try{
currentCursor = mDbHelper.fetchNewRows();
}catch(Exception e){}
return null;
}
@Override
protected void onPostExecute(String arg){
if(dialog != null && dialog.isShowing()){
dialog.dismiss();
}
if(currentCursor != null && currentCursor.getCount()>0){
String[] from = new String[]{DbHelper.KEY_ID, sortingColumn};
int[] to = new int[]{R.id.ai_about_author_btn, R.id.ai_author_name};
MyAlphabetizedAdapter notes = new MyAlphabetizedAdapter(this, R.layout.author_index_item, currentCursor, from, to, sortingColumn);
setListAdapter(notes);
}
}
}
On the first glance it works good however when I move fast between activities I sometimes get annoying Cursor Exceptions one saying this:
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):
java.lang.IllegalStateException: attempt to acquire a reference on a
close SQLiteClosable
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:31)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:56)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:49)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:49)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1118)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1092)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145): at
apt.tutorial.Restaurant.getAll(Restaurant.java:14)
and another one saying this (this happens a LOT on 2.1 devices):
09-03 11:30:19.713: INFO/dalvikvm(2541): Uncaught exception thrown by finalizer (will be discarded):
09-03 11:30:19.713: INFO/dalvikvm(2541): Ljava/lang/IllegalStateException;: Finalizing cursor android.database.sqlite.SQLiteCursor@45b99828 on null that has not been deactivated or closed
09-03 11:30:19.713: INFO/dalvikvm(2541): at android.database.sqlite.SQLiteCursor.finalize(SQLiteCursor.java:596)
09-03 11:30:19.713: INFO/dalvikvm(2541): at dalvik.system.NativeStart.run(Native Method)
What is more one of my ListActivities implements AlphabetIndexer where I have to pass the cursor. Very strange thing happens after I leave the activity and come back using back button. The list is populated (as expected) however when I try to use fast scrolling I get a null exception on the cursor even thought I recreated it and repopulated the list onResume().
For me it looks like OS cannot handle multiple cursors properly or maybe I miss something here?
Thanks for your help.
In general, I've had issues with Activities that try to keep multiple cursors alive. Especially when trying to use a managed cursor which I now try to avoid if possible (plus they're deprecated). Now, what I try to do is open the cursor, populate an array of objects with the data I need, close the cursor and then use an ArrayAdapter to populate the ListView. If you DO need to keep the cursor alive, and you should only need to if other activities on your stack are changing data, use a cursor manager or, better yet, the newer CursorLoader. When doing this, don't close the cursor, as your activity will manage the lifecycle of the cursor for you.
Since you start an async task and you leave the activity fast, means that when the task return you will have leave from this activity, thus onPause()
has been called and cursor is not closed. You could try to cancel the async task in the onPause()
.
精彩评论