“LoaderManager”中“initLoader”和“restartLoader”的区别

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (41)

我完全不明白initLoaderrestartLoader的职能LoaderManager:

  • 他们都有相同的签名。
  • restartLoader如果加载程序不存在,也会创建一个加载程序(“在此管理器中启动一个新的或重新启动一个现有的Loader”)。

这两种方法之间有什么关系吗?打电话restartLoader总是打电话initLoader是吗?我能打电话吗restartLoader不用打电话initLoader是吗?打电话省省吗?initLoader两次刷新数据?我什么时候应该使用其中之一和(重要!)为什么?

提问于
用户回答回答于

呼叫initLoader当Loader已经创建(例如,在配置更改之后)告诉LoaderManager将Loader的最新数据交付给onLoadFinished马上。如果尚未创建加载程序(例如,当活动/片段第一次启动时),则调用initLoader告诉LoaderManager调用onCreateLoader创建新的装载机。

呼叫restartLoader销毁已存在的Loader(以及与其相关的任何现有数据),并通知LoaderManager调用onCreateLoader来创建新的装载机并启动一个新的负载。

文档也非常清楚地说明了这一点:

  • initLoader确保装载机已初始化并处于活动状态。如果加载程序不存在,则创建一个加载程序,并启动加载程序(如果活动/片段当前已启动)。否则,最后创建的加载程序将被重用。
  • restartLoader在这个管理器中启动一个新的或重新启动一个现有的Loader,注册对它的回调,并且(如果活动/片段当前已经启动)开始加载它。如果先前启动了具有相同id的加载程序,则在新加载程序完成其工作时将自动销毁该加载程序。回调将在旧加载程序销毁之前交付。
用户回答回答于

要回答这个问题,需要深入了解LoaderManager代码。虽然LoaderManager本身的文档不够清楚(或者不存在这个问题),但是LoaderManagerImpl的文档(抽象LoaderManager的一个子类)更有启发性。

装载机

调用以使用Loader初始化特定ID。如果这个ID已经有一个与它相关联的Loader,那么它将保持不变,以前的回调将被新提供的回调替换。如果当前没有ID的加载程序,则创建并启动一个新的加载器。 该函数通常应在组件初始化时使用,以确保创建它所依赖的Loader。这允许它重用已有的装载机数据,例如,当一个活动在配置更改后被重新创建时,它就不需要重新创建它的加载器了。

重新启动装载机

调用以重新创建与特定ID关联的Loader。如果当前有与此ID相关联的加载程序,则将酌情取消/停止/销毁该ID。将创建一个具有给定参数的新Loader,一旦可用,就会将其数据传递给您。...调用此函数后,与此ID关联的任何以前的加载程序都将被视为无效,您将不会从它们收到进一步的数据更新。

基本上有两种情况:

  1. 带有id的加载程序不存在:这两种方法都将创建一个新的加载程序,因此没有区别
  2. 带有id的加载程序已经存在:initLoader将只替换作为参数传递的回调,而不会取消或停止加载程序。对于CursorLoader,这意味着游标保持打开和活动(如果在initLoader调用之前是这样的话)。另一方面,RestartLoader将取消、停止和销毁加载程序(并像游标一样关闭底层数据源),并创建一个新的加载器(如果加载程序是CursorLoader,它还会创建一个新的游标并重新运行查询)。

以下是这两种方法的简化代码:

装载机

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(...))。如果加载程序已经存在,则initLoader只替换回调(info.mCallback=...),而restartLoader则禁用旧加载程序(在新加载程序完成其工作时将销毁它),然后创建一个新加载程序。

因此,现在很清楚何时使用initLoader,何时使用restartLoader,以及为什么使用这两种方法是有意义的。如果不存在,则创建一个新的,如果一个已经存在,则被重用。除非需要新的加载程序,否则我们总是使用此方法,因为要运行的查询已经更改(不是底层数据,而是实际查询,如CursorLoader的SQL语句中的查询),在这种情况下,我们将调用restartLoader。

活动/片段生命周期与使用一种或另一种方法的决定无关(也不需要像Simon建议的那样使用一击标记来跟踪调用)!这一决定完全是基于对新装载机的“需求”。如果我们想运行相同的查询,我们使用initLoader,如果我们想运行不同的查询,我们使用restartLoader。我们总是可以使用RestartLoader,但这将是低效的。在屏幕旋转之后,或者如果用户离开应用程序,稍后返回到相同的活动,我们通常希望显示相同的查询结果,因此RestartLoader将不必要地重新创建加载程序,并排除底层(可能昂贵的)查询结果。

理解加载的数据与加载数据的“查询”之间的区别非常重要。假设我们使用CursorLoader查询订单表。如果向该表添加了新的订单,CursorLoader将使用onContentChanged()通知UI更新并显示新的顺序(在本例中无需使用restartLoader)。如果我们只想显示打开的订单,我们需要一个新的查询,我们将使用restartLoader返回一个新的CursorLoader,反映新的查询。

这两种方法之间有什么关系吗?

他们共享代码来创建一个新的Loader,但是当加载程序已经存在时,它们会执行不同的操作。

调用restartLoader总是调用initLoader吗?

不从来没有。

我可以调用restartLoader而不必调用initLoader吗?

调用initLoader两次刷新数据安全吗?

两次调用initLoader是安全的,但是不会刷新任何数据。

我什么时候应该使用其中之一和(重要!)为什么?

在我上面的解释之后,(希望)这应该是清楚的。

配置变化

LoaderManager在配置更改(包括方向更改)中保留其状态,因此您可能认为我们没有什么可做的了。再想想..。

首先,LoaderManager不会保留回调,所以如果什么都不做,就不会收到对诸如onLoadFined()之类的回调方法的调用,这很可能会破坏应用程序。因此,我们必须至少调用initLoader来恢复回调方法(当然,重新启动Loader也是可能的)。大

如果调用时调用方处于其启动状态,并且请求的加载程序已经存在并生成了其数据,则将立即(在此函数中)调用onLoadFined(Loader,D)

这意味着,如果我们在更改方向后调用initLoader,我们将立即得到onLoadFined调用,因为数据已经加载(假设在更改之前是这样的)。虽然这听起来很直接,但可能会很棘手(难道我们都喜欢Android...)。

我们必须区分两种情况:

  1. 处理配置更改本身:对于使用setRetainInstance(True)的片段,或者对于清单中使用了相应的Android:configChanges标记的活动,都是这样。这些组件在屏幕旋转后不会接收onCreate调用,所以请记住在另一个回调方法中调用initLoader/restartLoader(例如在onActivityCreated(Bundle)中)。为了能够初始化Loader,需要存储加载程序ID(例如,在列表中)。由于组件是跨配置更改保留的,所以我们只需循环现有加载程序ID并调用initLoader(loaderid。
  2. 不处理配置更改本身:在这种情况下,加载程序可以在onCreate中初始化,但我们需要手动保留加载程序ID,否则就无法进行所需的initLoader/restartLoader调用。如果ID存储在ArrayList中,我们将执行

扫码关注云+社区