java - LoaderManager中initLoader和restartLoader的区别

标签 java android android-loadermanager

我完全不知道 initLoader 之间的差异和 restartLoader LoaderManager 的功能:

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

  • 这两种方法之间有什么关系吗?是否拨打 restartLoader随时拨打 initLoader ?我可以打电话吗restartLoader无需调用 initLoader ?打电话安全吗initLoader两次刷新数据?我什么时候应该使用两者之一和 为什么 ?

    最佳答案

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

    initLoader

    Call to initialize a particular ID with a Loader. If this ID already has a Loader associated with it, it is left unchanged and any previous callbacks replaced with the newly provided ones. If there is not currently a Loader for the ID, a new one is created and started.

    This function should generally be used when a component is initializing, to ensure that a Loader it relies on is created. This allows it to re-use an existing Loader's data if there already is one, so that for example when an Activity is re-created after a configuration change it does not need to re-create its loaders.



    重启加载器

    Call to re-create the Loader associated with a particular ID. If there is currently a Loader associated with this ID, it will be canceled/stopped/destroyed as appropriate. A new Loader with the given arguments will be created and its data delivered to you once available.

    [...] After calling this function, any previous Loaders associated with this ID will be considered invalid, and you will receive no further data updates from them.



    基本上有两种情况:
  • 带有 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;
    }
    

    重启加载器
    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/Fragment 生命周期与使用一种或另一种方法的决定无关(并且没有必要像 Simon 建议的那样使用一次性标志来跟踪调用)!该决定完全基于对新装载机的“需求”。如果我们想运行相同的查询,我们使用 initLoader ,如果我们想运行不同的查询,我们使用 restartLoader .

    我们可以随时使用 restartLoader但这将是低效的。在屏幕旋转之后,或者如果用户离开应用程序并稍后返回到同一个 Activity,我们通常希望显示相同的查询结果,因此 restartLoader将不必要地重新创建加载器并消除底层(可能很昂贵)的查询结果。

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

    Is there some relation between the two methods?



    他们共享代码来创建一个新的加载器,但是当加载器已经存在时他们会做不同的事情。

    Does calling restartLoader always call initLoader?



    不,它永远不会。

    Can I call restartLoader without having to call initLoader?



    是的。

    Is it safe to call initLoader twice to refresh the data?



    可以安全拨打 initLoader两次,但不会刷新任何数据。

    When should I use one of the two and why?



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

    配置变更

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

    首先,LoaderManager 不保留回调,因此如果您什么都不做,您将不会收到对回调方法的调用,例如 onLoadFinished()等等,这很可能会破坏您的应用程序。

    因此,我们必须至少调用 initLoader恢复回调方法(当然,restartLoader 也是可能的)。 documentation指出:

    If at the point of call the caller is in its started state, and the requested loader already exists and has generated its data, then callback onLoadFinished(Loader, D) will be called immediately (inside of this function) [...].



    这意味着如果我们拨打 initLoader方向改变后,我们会得到一个 onLoadFinished立即调用,因为数据已经加载(假设在更改之前是这种情况)。
    虽然这听起来很直接,但可能很棘手(我们不是都喜欢 Android 吗...)。

    我们必须区分两种情况:
  • 自行处理配置更改:Fragment 就是这种情况
    使用 setRetainInstance(true) 或用于具有相应 android:configChanges 的 Activity list 中的标签。这些
    组件在例如之后不会收到 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 调用。
  • 关于java - LoaderManager中initLoader和restartLoader的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14445070/

    相关文章:

    java - 存储具有 K-V 列的表的最佳数据结构是什么

    android - 带有旋转机器人的自定义主菜单

    android - 上下滚动时加载程序不会停止向列表项添加 View

    android - fragment 中的 AsyncTaskLoader

    android - 提高谷歌地图绘制长路径的性能

    android - 我要使用什么 CursorAdapter?

    java - 如何使用选择按钮创建 JSF 表

    java - java中的简单十字准线

    java - Jetty 中不同端口上的独立 Web 应用程序

    android - Facebook 请求代码