【问题标题】:ListView filtering with CursorLoader AND FilterQueryProvider?使用 CursorLoader 和 FilterQueryProvider 进行 ListView 过滤?
【发布时间】:2013-12-05 11:51:46
【问题描述】:

在我们的项目中,我们经常处理列表,并在过去使用以下“模式”:

ListView 在一个 Fragment 中,在我们首先启动 CursorLoaders 的 onActivityCreated 中初始化,然后在 onFinish swapCusor 中初始化到 ListAdapter。然后我们使用 filterQueryProvider 实现了一个搜索功能,它只返回一个带有 contentResolver.query(...) 的游标。如果我在选择列表中的某些内容时进行了一些方向更改,我在很多情况下(不定期)会收到以下错误:

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 

这是在方向更改后在 onLoadFinished 中使用 swapCursor 时的情况。

我现在使用带有参数的 restartLoader 并使用带有附加路径的 Contacts.CONTENT_FILTER_URI 重新实现过滤器功能,这是约束,然后在 onLoadFinished 中交换此光标,因此我删除了 filterQueryProvider,它看起来工作正常。

问题是:是否有可能(或良好的做法)最初使用 CursorLoader 和 filterQueryProvider?还是我必须决定?因为当我只使用 filterQueryProvider 并且只使用约束 null 进行过滤时,我会达到相同的结果,这只会加载我所需的联系人列表并在之后进行过滤。

有什么建议吗?我没有通过谷歌找到这方面的数学信息;)

这是我当前的 LoaderCallbacks 实现顺便说一句:

 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 ");
        }
    };

【问题讨论】:

  • 您是否手动关闭游标/数据库?我已经读过 CursorLoader 自己做这个,所以你不必弄乱它。
  • 认为这是问题所在,但我没有在任何地方这样做。我认为这是加载程序光标和 filterQueryProviders“unmanagedCursor”的组合。有这方面的经验吗?
  • 我认为这里的一个困惑是,当使用 CursorLoader 时,不需要 FilterQueryProvider。一直读下去,包括 onCreateLoader:developer.android.com/guide/components/loaders.html#restarting。本质上,过滤器是在创建 CursorLoader 时应用的。所以,不需要FilterQueryProvider,当过滤字符串改变时使用restartLoader。

标签: android list listview filter android-cursorloader


【解决方案1】:

当使用 FilterQueryProvider 时,使用 restartLoader 而不是 initLoader

【讨论】:

  • 您的意思是在 onActivityCreated 中重新启动Loader,然后使用带有 unmanagedCursor 的 filterQueryProvider 吗?是否有可能在过滤器方法中使用 CursorLoader?
  • 是的,没有。在后一种情况下,您不需要 CursorLoader,因为 FQP 的 runQuery 正在单独的非 UI 线程中运行
  • 好的,所以您建议(或说这是一种有效的可能性),我只使用 filterQueryProvider,对吗?我只是有一个问题,它最初没有填满列表,我不得不调用 filter(null) 或 filter("") 一次,这似乎不是一个“好”的解决方案。但它在技术上确实和我猜的一样,列表是异步填充的..?
  • 你什么时候必须调用 filter() ?
  • 在用户点击 serach 字段中的键之前显示所有联系人。因为 filterQueryProvider 在设置为列表时最初不会提供结果。所以在用户点击一个键之前列表是空的。但是联系人列表在输入之前应该是可见的,这样用户也可以滚动浏览它。
【解决方案2】:

有点晚了,但我遇到了完全相同的问题,并且 cmets 不能很好地解释。对于那些面临问题的人,这里有解决方案:

“停止使用 FilterQueryProvider。CursorLoader 也可以替代它:用你的过滤器重新启动它,你会没事的。” (source)

这意味着;

而不是这样做:

@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&lt;Cursor&gt;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-25
    • 2012-10-11
    • 1970-01-01
    • 2013-03-16
    • 2019-01-31
    • 1970-01-01
    • 2018-03-11
    • 1970-01-01
    相关资源
    最近更新 更多