【发布时间】: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