LoaderManager 同时触发所有 Loader
LoaderManager fires all Loaders same time
首先让我描述一下我的情况:我收到了填充 RecyclerView
的类别列表。此列表中的每个 View
都是某种小部件,最常见的是另一个列表(水平 RecyclerView
)和一些新闻(也从 JSON 解析)。
我将所有内容保存在 ContentProvider
和 SQLiteDatabase
中(使用助手),其中包含两个 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 填充的,并且它们的 Loader
s 不会触发新闻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
内部可能是不可能的,但也许是另一种方式?通知 Loader
s 不仅通过他们的 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>
知道何时在游标上注册和取消注册其观察者的子类。
(根据之前的回答编辑)
首先让我描述一下我的情况:我收到了填充 RecyclerView
的类别列表。此列表中的每个 View
都是某种小部件,最常见的是另一个列表(水平 RecyclerView
)和一些新闻(也从 JSON 解析)。
我将所有内容保存在 ContentProvider
和 SQLiteDatabase
中(使用助手),其中包含两个 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 填充的,并且它们的 Loader
s 不会触发新闻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
内部可能是不可能的,但也许是另一种方式?通知 Loader
s 不仅通过他们的 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>
知道何时在游标上注册和取消注册其观察者的子类。
(根据之前的回答编辑)