I am just starting to play with the new cursorLoader and I am running into problems. The following code is just to understand how the cursorLoader works but I keep getting:
"Trying to requery and already closed cursor", when I resume this activity. The app was working fine before I started playing with the cursorLoader. Any ideas?
private Cursor getSectionData(CharSequence parent_id) {
String[] projection = new String[] {Titles.SECTION, Titles.TITLE, Titles._ID, Titles.CODE_RANGE,};
Uri titles = Titles.CONTENT_URI;
String select = "" + Titles.PARENT_ID + " match " + parent_id + "";
CursorLoader loader = new CursorLoader(this, titles, projection, select, null, null);
Cursor cTitles = loader.loadInBackground();
String[] projection1 = new String[] {Codes.CODE, Codes.EXCERPT, Codes._ID,};
Uri codes = Codes.CONTENT_URI;
String select1 = "" + Codes.PARENT_ID + " match " + parent_id + "";
CursorLoader loader1 = new CursorLoader(this, codes, projection1, select1, null, null);
Cursor cCodes = loader1.loadInBackground();
//Cursor cTitles = db.rawQuery("select section, title, _id, code_range from titles where parent_id match " + parent_id + "", null);
//startManagingCursor(cTitles);
//Cursor cCodes = db.rawQuery("select code, excerpt, _id from codes where parent_id match " + parent_id + "", null);
mQuery = "select code, excerpt, _id from codes where parent_id match " + parent_id + "";
//startManagingCursor(cCodes);
Cursor[] c = {cTitles, cCodes};
Cursor cursor = new MergeCursor(c);
startManagingCursor(cursor);
return cursor;
}
I don't believe that capturing the Cursor from loader.loadInBackground() is what you want.
The implementation for loadInBackground() basically does a query and returns you the cursor on the UI thread, which is what your trying to avoid.
Be cautious anytime you are waiting on the UI thread for a return value. It should be a good indicator that this is not something you want.
What I did to fix this issue was to restart the loader.
In my case, I'm trying to use the action bar to search my content.
My class extends ListFragment and implements LoaderManager.LoaderCallbacks
The Activity that houses this implements OnQueryTextListener and is calling into the fragment here is the code that I'm doing in the fragment.
public void doSearch(String query) {
Bundle bundle = new Bundle();
bundle.putString("query", query);
getLoaderManager().restartLoader(LoaderMangerIdHelper.INVENTORY, bundle, this);
}
Note that you must restart the loader. The loaders are all managed by the system. This will cause your onCreateLoader to get re-called. So you'll have to check for query string in there to set your selection.
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
String SELECTION = "someColumn=?";
List<String> selectionArgs = new ArrayList<String>();
selectionArgs.add("someArgs");
if (bundle != null && bundle.containsKey("query")) {
SELECTION += " AND columnTitle LIKE ?";
selectionArgs.add(bundle.getString("query") + "%");
}
final String[] SELECTION_ARGS = new String[selectionArgs.size()];
selectionArgs.toArray(SELECTION_ARGS);
mLoader = new CursorLoader(getActivity(), RoamPay.Product.CONTENT_URI, null, SELECTION,
SELECTION_ARGS, null);
return mLoader;
}
This starts the cursorLoading in the background. And the callbacks should be the same as usual.
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
// The list should now be shown.
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter.swapCursor(null);
}
You might also find your answer in this tutorial:
http://mobile.tutsplus.com/tutorials/android/android-sdk_loading-data_cursorloader/