This is a follow up to my question here: How to close a cursor used in a for loop
The responses solved the "Cursor finalized without prior close" warning but it has caused a StaleDataException in a very particular situation.
If the list has been scrolled, this cursor closed...
Cursor cursor = null;
cursor = (Cursor) getListView().getItemAtPosition(n);
//do something
if(cursor != null) {
cursor.close();
}
and the fragment backgrounded I get the following error:
09-15 21:16:58.240: E/test(21621): Exception
FATAL EXCEPTION: main
android.database.StaleDataException: Attempted to access a cursor after it has been closed.
at android.database.BulkCursorToCursorAdaptor.throwIfCursorIsClosed(BulkCursorToCursorAdaptor.java:64)
at android.database.BulkCursorToCursorAdaptor.getCount(BulkCursorToCursorAdaptor.java:70)
at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:196)
at android.database.CursorWrapper.moveToPosition(CursorWrapper.java:162)
at android.support.v4.widget.CursorAdapter.getItemId(CursorAdapter.java:225)
at android.widget.AbsListView.onSaveInstanceState(AbsListView.java:1782)
at android.view.View.dispatchSaveInstanceState(View.java:11950)
at android.view.ViewGroup.dispatchFreezeSelfOnly(ViewGroup.java:2685)
at android.widget.AdapterView.dispatchSaveInstanceState(AdapterView.java:782)
at android.view.ViewGroup.dispatchSaveInstanceState(ViewGroup.java:2671)
at android.view.View.saveHierarchyState(View.java:11933)
at android.support.v4.app.FragmentManagerImpl.saveFragmentViewState(FragmentManager.java:1608)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1004)
at android.support.v4.app.FragmentManagerImpl.removeFragment(FragmentManager.java:1212)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:639)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1478)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:446)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4793)
This question seems to relate to a similar issue: Merging cursors during onLoadFinished() causes StaleDataException after rotation but it relates to merging cursors and suggests using swapCursor, I'm not sure how I would apply that to this situation.
My thoughts are that getListView().getItemAtPosition(n)
must be returning a reference to the cursor rather than a new cursor and so when the activity is backgrounded and tries to access the now closed cursor whilst saving the fragment state it crashes. As mentioned earlier, it only crashes if the list view has been scrolled, I'm not sure why that should affect it.
How do I correctly close the cursor without causing a crash?
EDIT code in response to a comment asking to see how the cursor was loaded:
String[] desired_columns = {
MediaStore.Audio.Media._ID, //this column is needed, even though it won't be displayed so the cursor can populate the listview
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.DURATION
};
String selectionStatement = MediaStore.Audio.Media.IS_MUSIC + " != ? AND " + MediaStore.Audio.Media.DURATION + " > ?";
String[] selectionArguments = new String[2];
selectionArguments[0] = "0";
selectionArguments[1] = "7000";
Cursor myCursor = getActivity().getContentResolver().query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
desired_columns,
selectionStatement, //selection criteria
selectionArguments, //selection arguments like with SQL PDO use ? in criteria and put user input here to avoid sql injection
MediaStore.Audio.AudioColumns.ARTIST + " ASC"); //sort order of results
//moveToFirst() returns false if the cursor is empty
if (myCursor != null && myCursor.moveToFirst()) {
customCursorAdapter myCursorAdapter = new customCursorAdapter(getActivity(), myCursor, 0);
setListAdapter(myCursorAdapter);
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
getListView().setSelector(R.drawable.list_selection_colouring);
}