加载器用过很多次了,SimpleCursorAdapter也应用了很多次,但是AndroidLoader的概念却一直理不清楚,这到底适合什么适合使用更是搞不清楚,优点是什么也没有想过,这里回顾一下。
Android加载器是从 android3.0 引入的概念。支持在Activity或者Fragment中异步加载数据。
官方文档中也详细地说明了它的特征:
- 可用于每个
Activity
和Fragment
。 - 支持异步加载数据。
- 监控其数据源并在内容变化时传递新结果。
- 在某一配置更改后重建加载器时,会自动重新连接上一个加载器的游标。 因此,它们无需重新查询其数据。
如何实现加载器?
————启动加载器————
LoaderManager
可在 Activity
或 Fragment
内管理一个或多个 Loader
实例。每个 Activity 或片段中只有一个 LoaderManager
。
通常,您会在 Activity 的 onCreate()
方法 或 片段的onActivityCreated()
方法内初始化 Loader
。您执行操作如下:
getLoaderManager().initLoader(0, null, this);
- 用于标识加载器的唯一 ID。在此示例中,ID 为 0。
- 在构建时提供给加载器的可选参数(在此示例中为
null
)。
LoaderManager.LoaderCallbacks
实现,LoaderManager
将调用此实现来报告加载器事件。在此示例中,本地类实现LoaderManager.LoaderCallbacks
接口,因此它会传递对自身的引用this
。
初始化加载器,回调接口实现3个方法
initLoader()
方法将返回已创建的
Loader
,但您不必捕获其引用。
LoaderManager
将自动管理加载器的生命周期。
LoaderManager
将根据需要启动和停止加载,并维护加载器的状态及其相关内容。 这意味着您很少直接与加载器进行交互(有关使用加载器方法调整加载器行为的示例,请参阅
LoaderThrottle
示例)。当特定事件发生时,您通常会使用
LoaderManager.LoaderCallbacks
方法干预加载进程。
————重启加载器————
当您使用 initLoader()
时(如上所述),它将使用含有指定 ID 的现有加载器(如有)。如果没有,则它会创建一个。但有时,您想舍弃这些旧数据并重新开始。
要舍弃旧数据,请使用 restartLoader()
。例如,当用户的查询更改时,此 SearchView.OnQueryTextListener
实现将重启加载器。 加载器需要重启,以便它能够使用修订后的搜索过滤器执行新查询:
the action bar search text has changed. Update
// the search filter, and restart the loader to do a new query
// with this filter.
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
getLoaderManager().restartLoader(0, null, this);
return true;
}
————使用 LoaderManager 回调————
LoaderManager.LoaderCallbacks
包括以下方法:
onCreateLoader()
:针对指定的 ID 进行实例化并返回新的 Loader
- 当您尝试访问加载器时(例如,通过
initLoader()
),该方法将检查是否已存在由该 ID 指定的加载器。 如果没有,它将触发LoaderManager.LoaderCallbacks
方法onCreateLoader()
。在此方法中,您可以创建新加载器。 通常,这将是CursorLoader
,但您也可以实现自己的Loader
子类。 -
// If non-null, this is the current filter the user has provided. String mCurFilter; ... public Loader<Cursor> onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. Uri baseUri; if (mCurFilter != null) { baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mCurFilter)); } else { baseUri = Contacts.CONTENT_URI; } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); }
onLoadFinished()
:将在先前创建的加载器完成加载时调用
- 当先前创建的加载器完成加载时,将调用此方法。该方法必须在为此加载器提供的最后一个数据释放之前调用。 此时,您应移除所有使用的旧数据(因为它们很快会被释放),但不要自行释放这些数据,因为这些数据归其加载器所有,其加载器会处理它们。
- 当加载器发现应用不再使用这些数据时,即会释放它们。 例如,如果数据是来自
CursorLoader
的一个游标,则您不应手动对其调用close()
。如果游标放置在CursorAdapter
中,则应使用swapCursor()
方法,使旧Cursor
不会关闭。
-
// This is the Adapter being used to display the list's data. SimpleCursorAdapter mAdapter; ... public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) mAdapter.swapCursor(data); }
onLoaderReset()
:将在先前创建的加载器重置且其数据因此不可用时调用
通过此回调,您可以了解何时将释放数据,因而能够及时移除其引用。
此实现调用值为 null
的swapCursor()
:
// This is the Adapter being used to display the list's data. SimpleCursorAdapter mAdapter; ... public void onLoaderReset(Loader<Cursor> loader) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. mAdapter.swapCursor(null); }