LoaderManager中的initLoader和restartLoader之间的区别 [英] Difference between initLoader and restartLoader in LoaderManager

查看:126
本文介绍了LoaderManager中的initLoader和restartLoader之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我完全迷失了LoaderManagerinitLoaderrestartLoader功能之间的区别:

I'm completely lost regarding the differences between the initLoader and the restartLoader functions of the LoaderManager:

  • 它们都具有相同的签名.
  • restartLoader还会创建一个加载器(如果它不存在)(在此管理器中启动新的或重新启动现有的加载器").
  • They both have the same signature.
  • restartLoader also creates a loader, if it does not exist ("Starts a new or restarts an existing Loader in this manager").

两种方法之间是否存在某些关系?呼叫restartLoader是否总是呼叫initLoader吗?是否可以在不致电initLoader的情况下致电restartLoader?两次调用initLoader刷新数据是否安全?什么时候应该使用两者之一,为什么为什么?

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? When should I use one of the two and why?

推荐答案

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

To answer this question you need to dig into the LoaderManager code. While the documentation for LoaderManager itself isn't clear enough (or there wouldn't be this question), the documentation for LoaderManagerImpl, a subclass of the abstract LoaderManager, is much more enlightening.

initLoader

调用以使用Loader初始化特定的ID.如果这个ID已经 有一个与之关联的加载器,它保持不变,并且之前的任何 回调被新提供的回调代替.如果没有 当前是ID的加载程序,将创建并启动一个新的ID.

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.

restartLoader

调用以重新创建与特定ID关联的加载程序.如果 当前有一个与此ID关联的加载程序,它将是 视情况取消/停止/销毁.一个带有 给定的参数将被创建并将其数据一次传递给您 可用.

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.

[...]调用此函数后,与此ID关联的所有以前的装载程序 将被视为无效,并且您将不会再收到任何数据 来自他们的更新.

[...] 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.

基本上有两种情况:

  1. 具有id的加载程序不存在:这两种方法都会创建一个新的加载程序,因此两者之间没有区别
  2. 具有ID的加载程序已经存在:initLoader将仅替换作为参数传递的回调,而不会取消或停止加载程序.对于CursorLoader,这意味着光标保持打开状态并处于活动状态(如果在initLoader调用之前就是这种情况).另一方面,`restartLoader将取消,停止和销毁加载器(并像游标一样关闭底层数据源)并创建一个新的加载器(如果加载器为,还将创建一个新的游标并重新运行查询)一个CursorLoader).
  1. The loader with the id doesn't exist: both methods will create a new loader so there's no difference there
  2. The loader with the id already exists: initLoader will only replace the callbacks passed as a parameter but won't cancel or stop the loader. For a CursorLoader that means the cursor stays open and active (if that was the case before the initLoader call). `restartLoader, on the other hand, will cancel, stop and destroy the loader (and close the underlying data source like a cursor) and create a new loader (which would also create a new cursor and re-run the query if the loader is a CursorLoader).

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

Here's the simplified code for both methods:

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;
}

restartLoader

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);

如我们所见,如果加载程序不存在(信息== null),则这两种方法都将创建一个新的加载程序(信息= createAndInstallLoader(...)). 如果加载程序已经存在,则initLoader仅替换回调(info.mCallbacks = ...),而restartLoader停用旧的加载程序(新加载程序完成工作时将销毁它),然后创建一个新的.

As we can see in case the loader doesn't exist (info == null) both methods will create a new loader (info = createAndInstallLoader(...)). In case the loader already exists initLoader only replaces the callbacks (info.mCallbacks = ...) while restartLoader inactivates the old loader (it will be destroyed when the new loader completes its work) and then creates a new one.

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

Thus said it's now clear when to use initLoader and when to use restartLoader and why it makes sense to have the two methods. initLoader is used to ensure there's an initialized loader. If none exists a new one is created, if one already exists it's re-used. We always use this method UNLESS we need a new loader because the query to run has changed (not the underlying data but the actual query like in SQL statement for a CursorLoader), in which case we will call restartLoader.

活动/片段生命周期"与使用一种或另一种方法的决定无关(并且不必像Simon所建议的那样使用一次触发标志来跟踪呼叫)!仅根据对新装载机的需求"来做出此决定.如果要运行相同的查询,请使用initLoader;如果要运行不同的查询,请使用restartLoader.

The Activity/Fragment life cycle has nothing to do with the decision to use one or the other method (and there's no need to keep track of the calls using a one-shot flag as Simon suggested)! This decision is made solely based on the "need" for a new loader. If we want to run the same query we use initLoader, if we want to run a different query we use restartLoader.

我们总是可以使用restartLoader,但是那样效率很低.屏幕旋转后,或者如果用户离开应用程序并稍后返回相同的Activity,我们通常希望显示相同的查询结果,因此restartLoader会不必要地重新创建加载程序并消除底层(可能很昂贵)查询结果.

We could always use restartLoader but that would be inefficient. After a screen rotation or if the user navigates away from the app and returns later to the same Activity we usually want to show the same query result and so the restartLoader would unnecessarily re-create the loader and dismiss the underlying (potentially expensive) query result.

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

It's very important to understand the difference between the data that is loaded and the "query" to load that data. Let's assume we use a CursorLoader querying a table for orders. If a new order is added to that table the CursorLoader uses onContentChanged() to inform the UI to update and show the new order (no need to use restartLoader in this case). If we want to display only open orders we need a new query and we would use restartLoaderto return a new CursorLoader reflecting the new query.

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

Is there some relation between the two methods?

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

They share the code to create a new Loader but they do different things when a loader already exists.

打电话给restartLoader总是打电话给initLoader吗?

Does calling restartLoader always call initLoader?

不,永远不会.

我可以在不致电initLoader的情况下致电restartLoader吗?

Can I call restartLoader without having to call initLoader?

是的

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

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

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

It's safe to call initLoader twice but no data will be refreshed.

何时应使用两者之一,为什么?


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


That should (hopefully) be clear after my explanations above.

配置更改

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

A LoaderManager retains its state across configuration changes (including orientation changes) so you would think there's nothing left for us to do. Think again...

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

First of all, a LoaderManager doesn't retain the callbacks, so if you do nothing you won't receive calls to your callback methods like onLoadFinished() and the like and that will very likely break your app.

因此,我们必须至少调用initLoader来恢复回调方法(当然也可以使用restartLoader).

Therefore we HAVE to call at least initLoader to restore the callback methods (a restartLoader is, of course, possible too). The documentation states:

如果在呼叫时呼叫者处于启动状态,并且 请求的加载程序已经存在并已生成其数据,然后 回调onLoadFinished(Loader, D)将立即被调用(内部 此功能的内容)[...].

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 ...).

That means if we call initLoader after an orientation change, we will get an onLoadFinished call right away because the data is already loaded (assuming that was the case before the change). While that sounds straight forward it can be tricky (don't we all love Android...).

我们必须区分两种情况:

We have to distinguish between two cases:

  1. 句柄配置本身会发生变化:片段就是这种情况 使用setRetainInstance(true)或清单中具有相应android:configChanges标记的Activity.这些 组件在例如之后将不会收到onCreate调用.一种 屏幕旋转,因此请记住致电 initLoader/restartLoader在另一种回调方法中(例如 onActivityCreated(Bundle)).为了能够初始化加载程序, 加载程序ID需要存储(例如在列表中).因为 我们可以通过配置更改保留组件 只需遍历现有的加载程序ID并调用initLoader(loaderid, ...).
  2. 不自行处理配置更改:在这种情况下, 加载程序可以在onCreate中初始化,但我们需要手动进行 保留加载程序ID,否则我们将无法进行所需的操作 initLoader/restartLoader调用.如果ID存储在 ArrayList,我们将做一个
    outState.putIntegerArrayList(loaderIdsKey, loaderIdsArray)在 onSaveInstanceState并还原onCreate中的ID: loaderIdsArray = savedInstanceState.getIntegerArrayList(loaderIdsKey)在我们做之前 initLoader调用.
  1. Handles configuration changes itself: this is the case for Fragments that use setRetainInstance(true) or for an Activity with the according android:configChanges tag in the manifest. These components won't receive an onCreate call after e.g. a screen rotation, so keep in mind to call initLoader/restartLoader in another callback method (e.g. in onActivityCreated(Bundle)). To be able to initialize the Loader(s), the loader ids need to be stored (e.g. in a List). Because the component is retained across configuration changes we can just loop over the existing loader ids and call initLoader(loaderid, ...).
  2. Doesn't handle configuration changes itself: In this case the Loaders can be initialized in onCreate but we need to manually retain the loader ids or we won't be able to make the needed initLoader/restartLoader calls. If the ids are stored in an ArrayList, we would do an
    outState.putIntegerArrayList(loaderIdsKey, loaderIdsArray) in onSaveInstanceState and restore the ids in onCreate: loaderIdsArray = savedInstanceState.getIntegerArrayList(loaderIdsKey) before we make the initLoader call(s).

这篇关于LoaderManager中的initLoader和restartLoader之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆