在onLoadFinished()期间合并游标会在轮换后导致StaleDataException

时间:2013-05-06 05:51:33

标签: android staledataexception matrixcursor mergecursor

我正在使用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合并到返回的游标会产生错误。

我的问题是,为什么?

如果返回任何结果,我希望能够向返回的光标添加其他项目,以便用户可以选择在几个网站上执行搜索。是否有更好的方法来合并游标,以便在轮换后不会出现此异常?

2 个答案:

答案 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内部工作原理的人可能会证实或否认我的推理。