initLoader 和 restartLoader Loader 在 LoaderManager 中的区别

2022-08-31 08:30:04

我完全迷失了关于 和 函数之间的差异:initLoaderrestartLoaderLoaderManager

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

这两种方法之间有某种关系吗?打电话总是打电话吗?我可以在不打电话的情况下打电话吗?调用两次刷新数据是否安全?我什么时候应该使用两者之一,为什么restartLoaderinitLoaderrestartLoaderinitLoaderinitLoader


答案 1

要回答这个问题,你需要深入研究代码。虽然LoaderManager本身的文档不够清晰(或者不会有这个问题),但LoaderManagerImpl的文档,抽象LoaderManager的子类,更具启发性。LoaderManager

initLoader

调用以使用加载程序初始化特定 ID。如果此 ID 已经具有与之关联的加载程序,则它将保持不变,并且任何以前的回调都将替换为新提供的回调。如果当前没有 ID 的加载程序,则会创建并启动一个新加载程序。

通常应在组件初始化时使用此函数,以确保创建它所依赖的加载程序。这允许它重用现有加载程序的数据(如果已有),这样例如,当在配置更改后重新创建活动时,它不需要重新创建其加载程序。

重新启动加载程序

调用以重新创建与特定 ID 关联的加载程序。如果当前有与此 ID 关联的加载程序,它将根据需要取消/停止/销毁。将创建一个具有给定参数的新加载程序,并在可用后将其数据传递给您。

[...]调用此函数后,与此 ID 关联的任何先前加载程序都将被视为无效,并且您将不会从它们那里收到进一步的数据更新。

基本上有两种情况:

  1. 具有id的加载程序不存在:这两种方法都将创建一个新的加载程序,因此没有区别
  2. ID 为 :的加载程序将仅替换作为参数传递的回调,但不会取消或停止加载程序。对于 a,这意味着游标保持打开和活动状态(如果调用之前是这种情况)。另一方面,'restartLoader将取消,停止和销毁加载程序(并像光标一样关闭基础数据源)并创建新的加载程序(如果加载程序是CursorLoader,这也将创建一个新的游标并重新运行查询)。initLoaderCursorLoaderinitLoader

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

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 = ...),同时停用旧加载程序(当新加载程序完成其工作时,它将被销毁),然后创建一个新加载程序。initLoaderrestartLoader

因此,现在已经很清楚何时使用,何时使用以及为什么拥有这两种方法有意义。 用于确保存在初始化的加载程序。如果不存在,则会创建一个新,如果已经存在,则会重新使用它。我们总是使用此方法,除非我们需要一个新的加载器,因为要运行的查询已更改(不是基础数据,而是像CursorLoader的SQL语句中那样的实际查询),在这种情况下,我们将调用 .initLoaderrestartLoaderinitLoaderrestartLoader

活动/片段生命周期与决定使用一种或另一种方法无关(并且没有必要像Simon建议的那样使用一次性标志来跟踪调用)!此决定仅基于对新加载器的“需求”。如果我们想运行我们使用的相同查询,如果我们想运行不同的查询,我们使用 。initLoaderrestartLoader

我们总是可以使用,但那将是低效的。屏幕旋转后,或者如果用户离开应用,稍后返回到同一活动,我们通常希望显示相同的查询结果,因此会不必要地重新创建加载程序并关闭基础(可能代价高昂)的查询结果。restartLoaderrestartLoader

了解加载的数据与加载该数据的“查询”之间的区别非常重要。假设我们使用 CursorLoader 查询订单表。如果将新订单添加到该表中,CursorLoader 将使用 onContentChanged() 通知 UI 更新并显示新订单(在这种情况下无需使用)。如果我们只想显示未结订单,我们需要一个新查询,我们将使用返回一个反映新查询的新 CursorLoader。restartLoaderrestartLoader


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

他们共享代码以创建新的加载程序,但是当加载程序已经存在时,它们会执行不同的事情。

打电话总是打电话吗?restartLoaderinitLoader

不,它永远不会。

我可以在不打电话的情况下打电话吗?restartLoaderinitLoader

是的。

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

可以安全地调用两次,但不会刷新任何数据。initLoader

我什么时候应该使用两者之一,为什么


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

配置更改

LoaderManager 在配置更改(包括方向更改)中保留其状态,因此您会认为我们无能为力。请再想一想...

首先,LoaderManager不会保留回调,因此,如果您不执行任何操作,则不会收到对回调方法的调用,例如之类的,这很可能会破坏您的应用程序。onLoadFinished()

因此,我们必须至少调用以恢复回调方法(当然,a也是可能的)。文档指出:initLoaderrestartLoader

如果在调用时调用方处于启动状态,并且请求的加载程序已经存在并已生成其数据,则将立即调用回调(在此函数内部)[...]。onLoadFinished(Loader, D)

这意味着如果我们在方向更改后调用,我们将立即收到调用,因为数据已经加载(假设在更改之前就是这种情况)。虽然这听起来很简单,但可能很棘手(我们不是都喜欢Android吗...initLoaderonLoadFinished

我们必须区分两种情况:

  1. 处理配置更改本身:对于使用 setRetainInstance(true) 的片段或清单中具有相应标记的活动,就是这种情况。这些组件在屏幕旋转后不会收到 onCreate 调用,因此请记住在另一个回调方法(例如 in 中)中调用。为了能够初始化加载程序,需要存储加载程序ID(例如,在列表中)。由于组件在配置更改期间被保留,因此我们可以遍历现有的加载程序ID并调用 。android:configChangesinitLoader/restartLoaderonActivityCreated(Bundle)initLoader(loaderid, ...)
  2. 本身不处理配置更改:在这种情况下,加载程序可以在 onCreate 中初始化,但我们需要手动保留加载程序 ID,否则我们将无法进行所需的 initLoader/restartLoader 调用。如果 id 存储在 ArrayList 中,我们将在 onSaveInstanceState 中执行操作
    ,并在 onCreate: 中恢复 id,然后再进行 initLoader 调用。outState.putIntegerArrayList(loaderIdsKey, loaderIdsArray)loaderIdsArray = savedInstanceState.getIntegerArrayList(loaderIdsKey)

答案 2

在已经创建加载程序时调用(例如,这通常发生在配置更改之后)告诉 LoaderManager 立即将加载程序的最新数据传递到。如果尚未创建加载程序(例如,当活动/片段首次启动时),则调用告诉 LoaderManager 调用以创建新的加载程序。initLoaderonLoadFinishedinitLoaderonCreateLoader

调用会销毁已经存在的加载程序(以及与之关联的任何现有数据),并告诉 LoaderManager 调用以创建新的加载程序并启动新的加载。restartLoaderonCreateLoader


文档对此也非常清楚:

  • initLoader确保加载程序已初始化并处于活动状态。如果加载程序尚不存在,则会创建一个加载程序,并且(如果活动/片段当前已启动)启动加载程序。否则,将重用上次创建的加载程序。

  • restartLoader在此管理器中启动新的加载程序或重新启动现有加载程序,注册回调,然后(如果活动/片段当前已启动)开始加载它。如果先前已启动具有相同 ID 的加载程序,则当新加载程序完成其工作时,它将自动销毁。回调将在旧加载程序被销毁之前传递。


推荐