3

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

我的问题是,为什么?

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

4

2 回答 2

2

如果您已经开始使用swapCursor()而不是changeCursor()到处使用,那么我希望您也开始在这些地方处理光标关闭。

changeCursor()将关闭旧光标,这是故意的,当您直接使用onLoadFinished(). 以这种方式完成,因此您不必担心关闭它。

当你旋转你的设备时,android系统会检查它上次发送给你的光标是否还没有关闭,并再次发送它,而不是花费资源创建一个新的。您的代码将此游标包装在 a 的新实例中,MergeCursor该实例被传递给changeCursor(),它发现 this 与之前获得的对象不同,并决定关闭旧实例。由于MergeCursor仅包装您传递的游标,而不是复制其中的数据,因此您的新实例现在包含(至少)一个关闭的游标。

要正确处理此问题,您需要编写一些自己的代码来检查您通过的游标是否onLoadFinished()与当前实例中的游标相同MergeCursor,并且只有在获得新游标时才关闭现有实例。当然,您还需要跟踪包装在同一MergeCursor实例中的其他游标。

于 2016-05-18T09:28:27.220 回答
1

几天前这个问题再次出现,我很幸运地偶然发现了一个解决方案。

我发现我应该使用 swapCursor() 而不是 changeCursor()。根据 Android 文档

交换一个新的光标,返回旧的光标。与 changeCursor(Cursor) 不同,返回的旧 Cursor 没有关闭。

...

如果给定的新 Cursor 与先前设置的 Cursor 实例相同,则也返回 null。

最后一部分似乎是关键。上面问题中提到的错误可以追溯到 CursorAdapter 在合并光标上阻塞,因为它在旋转后尝试重绘片段时被关闭。通过改用 swapCursor(),CursorAdapter 能够重用“旧”合并游标,而不是质疑其有效性并抛出 StaleDataException。

我在这里做一些假设;也许更了解 Android 内部运作的人可以证实或否认我的推理。

于 2013-07-25T21:00:37.663 回答