要回答这个问题,你需要深入研究代码。虽然LoaderManager本身的文档不够清晰(或者不会有这个问题),但LoaderManagerImpl的文档,抽象LoaderManager的子类,更具启发性。LoaderManager
initLoader
调用以使用加载程序初始化特定 ID。如果此 ID 已经具有与之关联的加载程序,则它将保持不变,并且任何以前的回调都将替换为新提供的回调。如果当前没有 ID 的加载程序,则会创建并启动一个新加载程序。
通常应在组件初始化时使用此函数,以确保创建它所依赖的加载程序。这允许它重用现有加载程序的数据(如果已有),这样例如,当在配置更改后重新创建活动时,它不需要重新创建其加载程序。
重新启动加载程序
调用以重新创建与特定 ID 关联的加载程序。如果当前有与此 ID 关联的加载程序,它将根据需要取消/停止/销毁。将创建一个具有给定参数的新加载程序,并在可用后将其数据传递给您。
[...]调用此函数后,与此 ID 关联的任何先前加载程序都将被视为无效,并且您将不会从它们那里收到进一步的数据更新。
基本上有两种情况:
- 具有id的加载程序不存在:这两种方法都将创建一个新的加载程序,因此没有区别
- ID 为 :的加载程序将仅替换作为参数传递的回调,但不会取消或停止加载程序。对于 a,这意味着游标保持打开和活动状态(如果调用之前是这种情况)。另一方面,'restartLoader将取消,停止和销毁加载程序(并像光标一样关闭基础数据源)并创建新的加载程序(如果加载程序是CursorLoader,这也将创建一个新的游标并重新运行查询)。
initLoader
CursorLoader
initLoader
下面是这两种方法的简化代码:
initLoader
LoaderInfo info = mLoaders.get(id);
if (info == null) {
// Loader doesn't already exist -> create new one
info = createAndInstallLoader(id, args, LoaderManager.LoaderCallbacks<Object>)callback);
} else {
// Loader exists -> only replace callbacks
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
}
重新启动加载程序
LoaderInfo info = mLoaders.get(id);
if (info != null) {
LoaderInfo inactive = mInactiveLoaders.get(id);
if (inactive != null) {
// does a lot of stuff to deal with already inactive loaders
} else {
// Keep track of the previous instance of this loader so we can destroy
// it when the new one completes.
info.mLoader.abandon();
mInactiveLoaders.put(id, info);
}
}
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
正如我们所看到的,如果加载器不存在(info == null),这两种方法都将创建一个新的加载程序(info = createAndInstallLoader(...))。如果加载程序已经存在,则仅替换回调(info.mCallbacks = ...),同时停用旧加载程序(当新加载程序完成其工作时,它将被销毁),然后创建一个新加载程序。initLoader
restartLoader
因此,现在已经很清楚何时使用,何时使用以及为什么拥有这两种方法有意义。 用于确保存在初始化的加载程序。如果不存在,则会创建一个新,如果已经存在,则会重新使用它。我们总是使用此方法,除非我们需要一个新的加载器,因为要运行的查询已更改(不是基础数据,而是像CursorLoader的SQL语句中那样的实际查询),在这种情况下,我们将调用 .initLoader
restartLoader
initLoader
restartLoader
活动/片段生命周期与决定使用一种或另一种方法无关(并且没有必要像Simon建议的那样使用一次性标志来跟踪调用)!此决定仅基于对新加载器的“需求”。如果我们想运行我们使用的相同查询,如果我们想运行不同的查询,我们使用 。initLoader
restartLoader
我们总是可以使用,但那将是低效的。屏幕旋转后,或者如果用户离开应用,稍后返回到同一活动,我们通常希望显示相同的查询结果,因此会不必要地重新创建加载程序并关闭基础(可能代价高昂)的查询结果。restartLoader
restartLoader
了解加载的数据与加载该数据的“查询”之间的区别非常重要。假设我们使用 CursorLoader 查询订单表。如果将新订单添加到该表中,CursorLoader 将使用 onContentChanged() 通知 UI 更新并显示新订单(在这种情况下无需使用)。如果我们只想显示未结订单,我们需要一个新查询,我们将使用返回一个反映新查询的新 CursorLoader。restartLoader
restartLoader
这两种方法之间有某种关系吗?
他们共享代码以创建新的加载程序,但是当加载程序已经存在时,它们会执行不同的事情。
打电话总是打电话吗?restartLoader
initLoader
不,它永远不会。
我可以在不打电话的情况下打电话吗?restartLoader
initLoader
是的。
调用两次刷新数据是否安全?initLoader
可以安全地调用两次,但不会刷新任何数据。initLoader
我什么时候应该使用两者之一,为什么?
在我上面的解释之后,这应该(希望)很清楚。
配置更改
LoaderManager 在配置更改(包括方向更改)中保留其状态,因此您会认为我们无能为力。请再想一想...
首先,LoaderManager不会保留回调,因此,如果您不执行任何操作,则不会收到对回调方法的调用,例如之类的,这很可能会破坏您的应用程序。onLoadFinished()
因此,我们必须至少调用以恢复回调方法(当然,a也是可能的)。文档指出:initLoader
restartLoader
如果在调用时调用方处于启动状态,并且请求的加载程序已经存在并已生成其数据,则将立即调用回调(在此函数内部)[...]。onLoadFinished(Loader, D)
这意味着如果我们在方向更改后调用,我们将立即收到调用,因为数据已经加载(假设在更改之前就是这种情况)。虽然这听起来很简单,但可能很棘手(我们不是都喜欢Android吗...initLoader
onLoadFinished
我们必须区分两种情况:
- 处理配置更改本身:对于使用 setRetainInstance(true) 的片段或清单中具有相应标记的活动,就是这种情况。这些组件在屏幕旋转后不会收到 onCreate 调用,因此请记住在另一个回调方法(例如 in 中)中调用。为了能够初始化加载程序,需要存储加载程序ID(例如,在列表中)。由于组件在配置更改期间被保留,因此我们可以遍历现有的加载程序ID并调用 。
android:configChanges
initLoader/restartLoader
onActivityCreated(Bundle)
initLoader(loaderid,
...)
- 本身不处理配置更改:在这种情况下,加载程序可以在 onCreate 中初始化,但我们需要手动保留加载程序 ID,否则我们将无法进行所需的 initLoader/restartLoader 调用。如果 id 存储在 ArrayList 中,我们将在 onSaveInstanceState 中执行操作
,并在 onCreate: 中恢复 id,然后再进行 initLoader 调用。outState.putIntegerArrayList(loaderIdsKey, loaderIdsArray)
loaderIdsArray =
savedInstanceState.getIntegerArrayList(loaderIdsKey)