我正在使用loaderManager从数据库加载一些结果。不幸的是,以下代码在旋转设备后会产生 StaleDataException :
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor)
{
// If we've returned results, then pass them and the webSearches cursor along to be displayed
if(cursor.moveToFirst())
{
// Get a cursor containing additional web searches and merge it at the end of the results cursor
MatrixCursor searchesCursor = getWebSearchesCursor(searchTerm, false);
Cursor[] cursors = { cursor, searchesCursor };
// TODO: Figure out why merging cursors here causes staledataexception after rotation
Cursor results = new MergeCursor(cursors);
// Display the cursor in the ListView
adapter.changeCursor(results);
}
// If no results were returned, then return suggestions for web searches
else
{
// Get a cursor containing additional web searches
MatrixCursor noResults = getWebSearchesCursor(searchTerm, true);
adapter.changeCursor(noResults);
}
// Show the listView and hide the progress spinner
toggleListView(SHOW);
}
对 getWebSearchesCursor()的调用会返回一个MatrixCursor,其中包含一些其他搜索提示以附带任何返回的结果。我发现将 adapter.changeCursor(results)更改为 adapter.changeCursor(cursor)可以修复错误,因此看起来将MatrixCursor合并到返回的游标会产生错误。
我的问题是,为什么?
如果返回任何结果,我希望能够向返回的光标添加其他项目,以便用户可以选择在几个网站上执行搜索。是否有更好的方法来合并游标,以便在轮换后不会出现此异常?
答案 0 :(得分:2)
如果您已开始在任何地方使用swapCursor()
代替changeCursor()
,那么我希望您也开始在这些地方处理光标关闭。
changeCursor()
将关闭旧光标,当您直接使用onLoadFinished()
提供的光标时,这是有意的并且完美无缺。它以这种方式完成,所以你不必担心关闭它。
当您旋转设备时,Android系统将检查它上次发送的光标是否尚未关闭,并再次发送,而不是花费资源来创建新设备。您的代码将此光标包装在MergeCursor
的新实例中,该实例将传递给changeCursor()
,后者发现这与之前获得的对象不同,并决定关闭旧实例。由于MergeCursor
仅包装您传递的游标,而不是复制其中的数据,因此您的新实例现在包含(至少)一个关闭的游标。
要正确处理此问题,您需要编写一些自己的代码,以检查通过onLoadFinished()
的光标是否与当前MergeCursor
实例中的光标相同,并且只有在获得新光标时才关闭现有实例。当然,您还需要跟踪包含在同一MergeCursor
实例中的其他游标。
答案 1 :(得分:1)
这个问题在几天前再次出现,我有幸偶然发现了一个解决方案。
我发现我应该使用swapCursor()而不是changeCursor()。 According to the Android docs:
交换一个新的Cursor,返回旧的Cursor。与changeCursor(Cursor)不同,返回的旧Cursor不会关闭。
...
如果给定的新Cursor与之前设置的Cursor相同,则返回null。
最后一部分似乎是关键。上面问题中提到的错误可以追溯到合并游标上的CursorAdapter阻塞,因为它在旋转后尝试重绘片段时被关闭。通过使用swapCursor()代替,CursorAdapter能够重用“旧”合并游标,而不是质疑其有效性并抛出StaleDataException。
我在这里做一些假设;或许更了解Android内部工作原理的人可能会证实或否认我的推理。