我完全不了解initLoaderrestartLoaderLoaderManager函数之间的区别:

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

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

    最佳答案

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

    initLoader



    restart加载程序



    基本上有两种情况:

  • 带有id的加载器不存在:两种方法都将创建一个新的加载器,因此
  • 没有区别
  • 具有ID的加载程序已经存在: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吗?)。

    我们必须区分两种情况:
  • 处理配置本身会发生变化:Fragments就是这种情况
    使用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调用。
  • 09-30 00:06