LoaderManager 同时触发所有 Loader

LoaderManager fires all Loaders same time

首先让我描述一下我的情况:我收到了填充 RecyclerView 的类别列表。此列表中的每个 View 都是某种小部件,最常见的是另一个列表(水平 RecyclerView)和一些新闻(也从 JSON 解析)。

我将所有内容保存在 ContentProviderSQLiteDatabase 中(使用助手),其中包含两个 tables - 用于类别和新闻(每个都有类别 id 键) .所以我为每个小部件使用 Loader 从数据库加载数据,或者如果丢失我正在下载正确的数据并插入到数据库中。然后加载程序自动刷新列表提供新的游标。

屏幕上可能会立即出现两个(或更多)水平新闻列表。这些列表中的每一个都在注册自己的 Loader,如下所示:

loaderManager.initLoader(getWidgetCategory().getCategoryID(), null, getLoader());

getLoader(); 下我们有相同的 Loader(对于带有新闻的小部件),如下所示:

private class NewsLoader implements LoaderManager.LoaderCallbacks<Cursor> {

    @Override
    public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
        return new CursorLoader(getContext(),
                CustomContract.News.CONTENT_URI,
                CustomContract.News.PROJECTION_ALL,
                CustomContract.News.KEY_CATEGORY_ID+"=?",
                new String[]{String.valueOf(getWidgetCategory().getCategoryID())},
                CustomContract.News.ORDER_BY_DEFAULT
        );
    }

    @Override
    public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) {
        if (data.getCount()>=15) {
            swapCursor(data);
            displayContent();
        }
    }

    @Override
    public void onLoaderReset(final Loader<Cursor> loader) {
        swapCursor(null);
    }
}

问题是:当我只从一个类别下载新闻并将其插入数据库时​​,所有 Loader 都在触发刷新列表,这对性能来说真的很糟糕......例如 - 有两个屏幕上有新闻的水平列表,我们向下滚动一点,另一个(第三个)即将到来。它没有任何消息,所以后台任务正在下载它们并插入数据库,然后 LoaderManager 为第三个小部件触发 Loader。但不仅...而且附加到上面两个列表的 Loaders 正在触发刷新自己的列表。

我假设相同的 CONTENT_URI 存在问题,可能 CursorLoader 构造函数中的其他参数也存在问题。唯一的变化(对于每个新闻水平列表小部件)是 WHERE 子句具有不同的类别 ID,并且所有 Loader 都从同一个数据库 table 获取内容。我怎样才能只触发一个 Loader 小部件,哪个 downloaded/inserted 新闻?请注意,已注册加载程序的 id 和 WHERE 选择是相同的...

就像我写的那样,可能有不同类型的小部件(还有列表,但不包括新闻),这些小部件是从其他 tables 填充的,并且它们的 Loaders 不会触发新闻Loader秒。我假设如果我得到只有文本小部件的类别列表(也共享数据库 table),下载其中一个小部件的文本将触发所有 `Loader' 并重绘其他小部件。

编辑:

换句话说,在我的自定义 ContentProvider 中,我重写了 query 方法,其中包含行:

@Override
public Cursor query(
        final Uri uri,
        final String[] projection,
        final String selection,
        final String[] selectionArgs,
        String sortOrder
) {
    //selection, etc.
    //newly created cursor from selection above
    cursor.setNotificationUri(getContext().getContentResolver(), uri);
    return cursor;
}

并且它通知所有观察 Loader 已通过 uri。我有几个列表(实际上这些是 RecyclerView 并不重要)它们使用来自一个 URI 的数据,但将自定义选择参数传递给每个 CursorLoader。但是 URI 对所有加载器都是通用的,所以所有加载器都会收到通知,我只想通知这个加载器,它触发了数据库 activity。我只想通知一个具有已知 id 的 Loader,它也被传递给 selectionArgs 中的 query 方法。是的,我知道这在 query 内部可能是不可能的,但也许是另一种方式?通知 Loaders 不仅通过他们的 URI

URI is common for all so all loaders are notified and I want to notify only this one, which fired database activity.

最简单的解决方法是创建一个 URI 模式,其中每个类别的 URI 都不同,因此当您调用 Cursor.setNoficationUri() 时,每个游标(以及最终的每个加载程序)将只接收其数据的 ContentChanged 通知关注于。

没有办法关闭 CursorLoader 上的 ContentObserver,所以如果您绝对肯定不能有不同的 URI,那么我唯一可以建议的是写一个 Loader<Cursor> 知道何时在游标上注册和取消注册其观察者的子类。

(根据之前的回答编辑)