Android Loader加载器

前言

Linus Benedict Torvalds : RTFSC – Read The Funning Source Code

概述

一种能使在Activity或Fragment中异步加载数据更加容易的方法。通过它能在Activity或Fragment的生命周期间顺畅的加载数据。

加载器特性:

  1. 支持Activity和Fragment。
  2. 支持异步加载数据。
  3. 支持监视数据源的变动并在内容改变时传送新的结果。
  4. 支持在配置改变而被重新创建后,能自动重连到上一个加载器的游标,不必重新查询数据。

API 摘要

在应用中使用加载器时,会涉及到多个类和接口。 下表汇总了这些类和接口:

类/接口 说明
LoaderManager 一种与 Activity 或 Fragment 相关联的的抽象类,用于管理一个或多个 Loader 实例。 这有助于应用管理与 Activity 或 Fragment 生命周期相关联的、运行时间较长的操作。它最常见的用法是与 CursorLoader 一起使用,但应用可自由写入其自己的加载器,用于加载其他类型的数据。 每个 Activity 或片段中只有一个 LoaderManager。但一个 LoaderManager 可以有多个加载器。
LoaderManager.LoaderCallbacks 一种回调接口,用于客户端与 LoaderManager 进行交互。例如,您可使用 onCreateLoader() 回调方法创建新的加载器。
Loader 一种执行异步数据加载的抽象类。这是加载器的基类。 您通常会使用 CursorLoader,但您也可以实现自己的子类。加载器处于活动状态时,应监控其数据源并在内容变化时传递新结果。
AsyncTaskLoader 提供 AsyncTask 来执行工作的抽象加载器。
CursorLoader AsyncTaskLoader 的子类,它将查询 ContentResolver 并返回一个 Cursor。此类采用标准方式为查询游标实现 Loader 协议。它是以 AsyncTaskLoader 为基础而构建,在后台线程中执行游标查询,以免阻塞应用的 UI。使用此加载器是从 ContentProvider 异步加载数据的最佳方式,而不用通过片段或 Activity 的 API 来执行托管查询。

API 使用

启动加载器

LoaderManager 可在 Activity 或 Fragment 内管理一个或多个 Loader 实例。每个 Activity 或片段中只有一个 LoaderManager。
一般会在ActivityonCreate()方法或FragmentonActivityCreated()方法内初始化 Loader。操作如下:

1
2
3
4
5
// Prepare the loader. Either re-connect with an existing one, or start a new one.
getLoaderManager().initLoader(0, null, this);
// source code
public abstract <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback);

参数解析:

  • id:给予这个loader唯一的标识,可以是任何值。标识是作用在一个特定的LoaderManager实例。
  • args:在构建时提供给loader随意的数据支持。如果一个loader已经存在(新的将不被创建),这些参数将被忽略和最后一个数据将被继续使用。
  • callback:连接着LoaderManager的接口,LoaderManager将调用此实现来报告加载器事件。

无论何种情况,给定的LoaderManager.LoaderCallbacks实现均与加载器相关联,且将在加载器状态变化时调用。如果在调用时,调用程序处于启动状态,且请求的加载器已存在并生成了数据,则系统将立即调用onLoadFinished()(在initLoader()期间),因此您必须为此做好准备。

重启加载器

当一个loader已经存在,它将使用含有指定ID的现有加载器。但有些情况,要舍弃这些旧数据并重新开始。要舍弃旧数据,可以使用 restartLoader()。

1
2
3
4
getLoaderManager().restartLoader(0, null, this);
// source code
public abstract <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback);

参数如initLoader一样。

LoaderManager 回调

LoaderManager.LoaderCallbacks是一个支持客户端与LoaderManager交互的回调接口。

加载器(特别是 CursorLoader)在停止运行后,仍需保留其数据。这样,应用即可保留Activity或片段的onStop()onStart()方法中的数据。当用户返回应用时,无需等待它重新加载这些数据。可使用LoaderManager.LoaderCallbacks方法了解何时创建新加载器,并告知应用何时停止使用加载器的数据。

回调包括以下方法:

  • onCreateLoader():针对指定的 ID 进行实例化并返回新的 Loader。
  • onLoadFinished():将在先前创建的加载器完成加载时调用。
  • onLoaderReset():将在先前创建的加载器重置且其数据因此不可用时调用。

onCreateLoader:

1
2
3
4
5
6
7
8
/**
* Instantiate and return a new Loader for the given ID.
*
* @param id The ID whose loader is to be created.
* @param args Any arguments supplied by the caller.
* @return Return a new Loader instance that is ready to start loading.
*/
public Loader<D> onCreateLoader(int id, Bundle args)

onLoadFinished:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* Called when a previously created loader has finished its load. Note
* that normally an application is not allowed to commit fragment
* transactions while in this call, since it can happen after an
* activity's state is saved. See {@link FragmentManager#beginTransaction()
* FragmentManager.openTransaction()} for further discussion on this.
*
* This function is guaranteed to be called prior to the release of
* the last data that was supplied for this Loader. At this point
* you should remove all use of the old data (since it will be released
* soon), but should not do your own release of the data since its Loader
* owns it and will take care of that. The Loader will take care of
* management of its data so you don't have to. In particular:
*
* The Loader will monitor for changes to the data, and report
* them to you through new calls here. You should not monitor the
* data yourself. For example, if the data is a {@link android.database.Cursor}
* and you place it in a {@link android.widget.CursorAdapter}, use
* the {@link android.widget.CursorAdapter#CursorAdapter(android.content.Context,
* android.database.Cursor, int)} constructor without passing
* in either {@link android.widget.CursorAdapter#FLAG_AUTO_REQUERY}
* or {@link android.widget.CursorAdapter#FLAG_REGISTER_CONTENT_OBSERVER}
* (that is, use 0 for the flags argument). This prevents the CursorAdapter
* from doing its own observing of the Cursor, which is not needed since
* when a change happens you will get a new Cursor throw another call
* here.
* The Loader will release the data once it knows the application
* is no longer using it. For example, if the data is
* a {@link android.database.Cursor} from a {@link android.content.CursorLoader},
* you should not call close() on it yourself. If the Cursor is being placed in a
* {@link android.widget.CursorAdapter}, you should use the
* {@link android.widget.CursorAdapter#swapCursor(android.database.Cursor)}
* method so that the old Cursor is not closed.
*
* @param loader The Loader that has finished.
* @param data The data generated by the Loader.
*/
public void onLoadFinished(Loader<D> loader, D data);

onLoaderReset:

1
2
3
4
5
6
7
8
/**
* Called when a previously created loader is being reset, and thus
* making its data unavailable. The application should at this point
* remove any references it has to the Loader's data.
*
* @param loader The Loader that is being reset.
*/
public void onLoaderReset(Loader<D> loader);