Filtering a ListView with CursorLoader And FilterQueryProvider?

In our project, we deal with lists a lot and used it in the past with the following "template":

The ListView is in a fragment, initialized to onActivityCreated, where we first start CursorLoaders, and then to onFinish swapCusor in the ListAdapter. Then we implemented the search function using the filterQueryProvider filter, which simply returns the cursor with contentResolver.query (...). If I made some orientation changes while something in the list was selected, in many cases (not regularly) I received the following error:

android.database.StaleDataException: Attempted to access a cursor after it has been closed.
12-05 10:36:59.531: E/ACRA(12079):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1967)
12-05 10:36:59.531: E/ACRA(12079):  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1992)
12-05 10:36:59.531: E/ACRA(12079):  at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3378)
12-05 10:36:59.531: E/ACRA(12079):  at android.app.ActivityThread.access$700(ActivityThread.java:127)
12-05 10:36:59.531: E/ACRA(12079):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1162)
12-05 10:36:59.531: E/ACRA(12079):  at android.os.Handler.dispatchMessage(Handler.java:99)
12-05 10:36:59.531: E/ACRA(12079):  at android.os.Looper.loop(Looper.java:137)
12-05 10:36:59.531: E/ACRA(12079):  at android.app.ActivityThread.main(ActivityThread.java:4448)
12-05 10:36:59.531: E/ACRA(12079):  at java.lang.reflect.Method.invokeNative(Native Method)
12-05 10:36:59.531: E/ACRA(12079):  at java.lang.reflect.Method.invoke(Method.java:511)
12-05 10:36:59.531: E/ACRA(12079):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:823)
12-05 10:36:59.531: E/ACRA(12079):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:590)
12-05 10:36:59.531: E/ACRA(12079):  at dalvik.system.NativeStart.main(Native Method)
12-05 10:36:59.531: E/ACRA(12079): Caused by: android.database.StaleDataException: Attempted to access a cursor after it has been closed.
12-05 10:36:59.531: E/ACRA(12079):  at android.database.BulkCursorToCursorAdaptor.throwIfCursorIsClosed(BulkCursorToCursorAdaptor.java:75)
12-05 10:36:59.531: E/ACRA(12079):  at android.database.BulkCursorToCursorAdaptor.getColumnNames(BulkCursorToCursorAdaptor.java:170)
12-05 10:36:59.531: E/ACRA(12079):  at android.database.AbstractCursor.getColumnIndex(AbstractCursor.java:248)
12-05 10:36:59.531: E/ACRA(12079):  at android.database.AbstractCursor.getColumnIndexOrThrow(AbstractCursor.java:266)
12-05 10:36:59.531: E/ACRA(12079):  at android.database.CursorWrapper.getColumnIndexOrThrow(CursorWrapper.java:78)
12-05 10:36:59.531: E/ACRA(12079):  at android.support.v4.widget.CursorAdapter.swapCursor(CursorAdapter.java:344)
12-05 10:36:59.531: E/ACRA(12079):  at 

This is the case when using swapCursor in onLoadFinished after changing orientation.

restartLoader Contacts.CONTENT_FILTER_URI , , onLoadFinished, filterQueryProvider, .

: ( ) CursorLoader filterQueryProvider? ? , filterQueryProvider, null, .

? mathinc google;)

LoaderCallbacks. btw:

 private LoaderManager.LoaderCallbacks<Cursor> phoneBookContactsLoaderCallback = new LoaderManager.LoaderCallbacks<Cursor>() {

        @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
            // if(constraint != null && constraint.length() > 0) {
            // selection = Contacts.DISPLAY_NAME + " LIKE ?";
            // selectionArgs = new String [] {"%" + constraint + "%"};
            // }
            numLoaderManagersRunning++;
            String constraint = null;
            if(args != null){
                constraint = args.getString(CONSTRAINT);
            }

            Uri uri = null;

            if(constraint!= null && !constraint.isEmpty()){
                uri =  Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, constraint);
            } else {
                uri = Contacts.CONTENT_URI;
            }


            return new CursorLoader(getActivity(), uri , PROJECTION_PHONEBOOK_CONTACTS,
                    null, null, Contacts.DISPLAY_NAME + " COLLATE NOCASE ASC");
        }

        @Override
        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
            Logger.e(TAG, "Load finished ");
            // phoneBookContactsCursorAdapter.swapCursor(new MatrixCursor(new String [] { Contacts._ID,
            // Contacts.DISPLAY_NAME, Contacts.PHOTO_ID }));
            phoneBookContactsCursorAdapter.swapCursor(data);
            if (actualMultiFilterListener != null){
                actualMultiFilterListener.onFilterComplete(data.getCount());
            }
//filterList("");
            numLoaderManagersRunning--;

            if (numLoaderManagersRunning <= 0) {
                // The list should now be shown.
                if (isResumed()) {
                    setListShown(true);
                } else {
                    setListShownNoAnimation(true);
                }
            }
        }

        @Override
        public void onLoaderReset(Loader<Cursor> loader) {
//            phoneBookContactsCursorAdapter.swapCursor(null);
            Logger.e(TAG, "Load resetted ");
        }
    };
+4
2

, , . , , :

" FilterQueryProvider. CursorLoader : , ". ()

,

:

@Override
public Cursor runQuery(CharSequence listId) {
    return mDb.getContacts(listId);  
}// DO NOT set direct database query, has no observation at all.

@Override
public Cursor runQuery(CharSequence listId) {
    return getContentManager.query(....,new String[]{listId}...
}// DO NOT return content manager result, forgets observers.

@Override
public Cursor runQuery(CharSequence listId) {
    Bundle bundle = new Bundle();
    bundle.putCharSequence("selected", listId);
    getLoaderManager().restartLoader(CURSOR_LOADER_CONTACTS, bundle, MainActivity.this);
    return null;
}// DO NOT restart the cursor loader with new info, screws observers. 

mContactAdapter.getFilter().filter(id+""); ( )

:

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    switch (id) {
        case CURSOR_LOADER_CONTACTS:
            long mListId = 0;
            if (args != null) {
                mListId = args.getLong(CONTACT_FILTER);
            }
            return new CursorLoader(this, ContentProvider.CONTACT_DISPLAY_URI, null, ContactTable.COLUMN_GROUP_ID+"=?", new String[]{id+""}, null); 
    } //Apply filter here with new info comes from 'restartLoader'
    return null;
}

getLoaderManager().restartLoader(MainActivity.CURSOR_LOADER_CONTACTS, bundle, MainActivity.this);

, MainActivity.this LoaderManager.LoaderCallbacks<Cursor>

+2

FilterQueryProvider restartLoader initLoader

+4

All Articles