我完全不了解initLoader
的restartLoader
和LoaderManager
函数之间的区别:
restartLoader
还会创建一个加载器(如果它不存在)(“在此管理器中启动新的或重新启动现有的加载器”)。 两种方法之间有关系吗?调用
restartLoader
是否总是调用initLoader
吗?我可以在不调用restartLoader
的情况下调用initLoader
吗?两次调用initLoader
刷新数据是否安全?我什么时候应该使用和这两者之一? 最佳答案
要回答这个问题,您需要深入研究LoaderManager
代码。
尽管LoaderManager本身的文档不够清楚(或者不会有这个问题),但抽象LoaderManager的子类LoaderManagerImpl的文档却更具启发性。
initLoader
restart加载程序
基本上有两种情况:
initLoader
将仅替换作为参数传递的回调,而不会取消或停止加载程序。对于CursorLoader
,这意味着光标保持打开状态并处于 Activity 状态(如果在initLoader
调用之前是这种情况)。另一方面,`restartLoader将取消,停止和销毁加载器(并像游标一样关闭底层数据源)并创建一个新的加载器(如果加载器为,还将创建一个新的游标并重新运行查询)一个CursorLoader)。 这是这两种方法的简化代码:
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;
}
restart加载程序
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(...))。
如果加载程序已经存在,则
initLoader
仅替换回调(info.mCallbacks = ...),而restartLoader
停用旧的加载程序(新加载程序完成工作时将销毁它),然后创建一个新的加载程序。因此,现在很清楚何时使用
initLoader
和何时使用restartLoader
,以及为什么使用这两种方法才有意义。initLoader
用于确保存在初始化的加载程序。如果不存在,则创建一个新的,如果已经存在,则将其重新使用。我们始终使用此方法,除非需要一个新的加载器,因为要运行的查询已更改(不是基础数据,而是实际的查询(如CursorLoader的SQL语句中的实际查询)),在这种情况下,我们将调用restartLoader
。Activity /片段生命周期与使用一种或另一种方法的决定无关(并且不需要像Simon所建议的那样使用单发标志来跟踪调用)!仅根据对新装载机的“需求”来做出此决定。如果要运行相同的查询,请使用
initLoader
;如果要运行不同的查询,请使用restartLoader
。我们可以一直使用
restartLoader
,但是效率很低。屏幕旋转后,或者如果用户离开应用程序并稍后返回相同的Activity,我们通常希望显示相同的查询结果,因此restartLoader
将不必要地重新创建加载程序并消除基础(可能昂贵)的查询结果。了解已加载的数据与加载该数据的“查询”之间的区别非常重要。假设我们使用CursorLoader查询表中的订单。如果向该表添加了新订单,则CursorLoader使用onContentChanged()通知UI更新并显示新订单(在这种情况下,无需使用
restartLoader
)。如果我们只想显示未结订单,我们需要一个新查询,我们将使用restartLoader
返回一个反射(reflect)新查询的新CursorLoader。他们共享代码来创建新的Loader,但是当已有Loader时,他们会做不同的事情。
不,永远不会。
是的。
两次调用
initLoader
是安全的,但不会刷新任何数据。经过我上面的解释,这一点(希望)应该是清楚的。
配置更改
LoaderManager会在配置更改(包括方向更改)中保留其状态,因此您会认为我们已无事可做。再想一想...
首先,LoaderManager不会保留回调,因此,如果您不执行任何操作,将不会收到对诸如
onLoadFinished()
之类的回调方法的调用,这很可能会破坏您的应用程序。因此,我们必须至少调用
initLoader
来恢复回调方法(当然也可以使用restartLoader
)。 documentation指出:这意味着,如果在方向更改后调用
initLoader
,则会立即收到一个onLoadFinished
调用,因为已经加载了数据(假设在更改之前就是这种情况)。尽管听起来很简单,但可能会有些棘手(我们都不要喜欢Android吗?)。
我们必须区分两种情况:
使用setRetainInstance(true)或对 list 中具有相应
android:configChanges
标记的Activity进行操作。这些组件在例如之后将不会收到onCreate调用。一种
屏幕旋转,因此请记住致电
另一个回调方法中的
initLoader/restartLoader
(例如onActivityCreated(Bundle)
)。为了能够初始化加载程序,加载程序ID需要存储(例如在列表中)。因为
我们可以通过配置更改保留组件
只需遍历现有的加载程序ID并调用
initLoader(loaderid,...)
即可。 加载程序可以在onCreate中初始化,但我们需要手动进行
保留加载程序ID,否则我们将无法进行所需的操作
initLoader/restartLoader调用。如果ID存储在
ArrayList,我们会做一个
outState.putIntegerArrayList(loaderIdsKey, loaderIdsArray)
在onSaveInstanceState并还原onCreate中的ID:
loaderIdsArray =savedInstanceState.getIntegerArrayList(loaderIdsKey)
在我们做之前initLoader调用。