Loader(加载器)简介

Android 3.0 中引入了加载器,支持轻松在 Activity 或Fragment中异步加载数据。 加载器具有以下特征:
(1)可用于每个 Activity 和 Fragment。
(2)支持异步加载数据。

(3)监控其数据源并在内容变化时传递新结果。

(4)在某一配置更改后重建加载器时,会自动重新连接上一个加载器的 Cursor。 因此,它们无需重新查询其数据。

Loader API Summary(摘要)

在应用中使用加载器时,可能会涉及到多个类和接口。 下表汇总了这些类和接口:
上表中的类和接口是您在应用中用于实现加载器的基本组件。 并非您创建的每个加载器都要用到上述所有类和接口。但是,为了初始化加载器以及实现一个 Loader 类(如 CursorLoader),您始终需要要引用 LoaderManager。下文将为您展示如何在应用中使用这些类和接口。

Use the loader in the application(在应用中使用加载器)

此部分描述如何在 Android 应用中使用加载器。使用加载器的应用通常包括:
(1)Activity 或 Fragment。
(2)LoaderManager 的实例。
(3)一个 CursorLoader,用于加载由 ContentProvider 支持的数据。您也可以实现自己的 Loader 或 AsyncTaskLoader 子类,从其他源中加载数据。
(4)一个 LoaderManager.LoaderCallbacks 实现。您可以使用它来创建新加载器,并管理对现有加载器的引用。
(5)一种显示加载器数据的方法,如 SimpleCursorAdapter。
(6)使用 CursorLoader 时的数据源,如 ContentProvider。

1.Boot loader(启动加载器)

LoaderManager 可在 Activity 或 Fragment 内管理一个或多个 Loader 实例。每个 Activity 或Fragment只有一个 LoaderManager。

通常,您会使用 Activity 的 onCreate() 方法或Fragment的 onActivityCreated() 方法初始化 Loader。您执行操作如下:
// Prepare the loader.  Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);

initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback)方法采用以下参数:
(1)用于标识加载器的唯一 ID。在此示例中,ID 为 0。


(2)在构建时提供给加载器的可选参数(在此示例中为 null )。

(3)LoaderManager.LoaderCallbacks 实现, LoaderManager 将调用此实现来报告加载器事件。在此示例中,本地类实现 LoaderManager.LoaderCallbacks 接口,因此它会将引用 this 传递给自己。
initLoader() 调用确保加载器已初始化且处于Activity状态。这可能会出现两种结果:
(1)如果 ID 指定的加载器已存在,则将重复使用上次创建的加载器。

(2)如果 ID 指定的加载器不存在,则 initLoader() 将触发 LoaderManager.LoaderCallbacks 方法 onCreateLoader()。在此方法中,您可以实现代码以实例化并返回新加载器。有关详细介绍,请参阅 onCreateLoader 部分。
无论何种情况,给定的 LoaderManager.LoaderCallbacks 实现均与加载器相关联,且将在加载器状态变化时调用。如果在调用时,调用程序处于启动状态,且请求的加载器已存在并生成了数据,则系统将立即调用 LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished() (在 initLoader() 期间),因此您必须为此做好准备。有关此回调的详细介绍,请参阅 onLoadFinished。
请注意,initLoader() 方法将返回已创建的 Loader,但您不必捕获其引用。LoaderManager 将自动管理加载器的生命周期。LoaderManager 将根据需要启动和停止加载,并维护加载器的状态及其相关内容。 这意味着您很少直接与加载器进行交互。当特殊事件发生时,您通常会使用 LoaderManager.LoaderCallbacks 方法干预加载进程。有关此主题的详细介绍,请参阅使用 LoaderManager 回调。

2.Restart loader(重启加载器)

当您使用 initLoader() 时(如上所述),它将使用含有指定 ID 的现有加载器(如有)。如果没有,则它会创建一个。但有时,您想放弃这些旧数据并重新开始。

要放弃旧数据,请使用 restartLoader()。例如,当用户的查询更改时,此 SearchView.OnQueryTextListener 实现将重启加载器。 加载器需要重启,以便它能够使用修订后的搜索筛选器执行新查询:

public boolean onQueryTextChanged(String newText) {// Called when 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;
}

3.Use LoaderManager callback(使用 LoaderManager 回调)

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

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

LoaderManager.LoaderCallbacks 包括以下方法:
(1)onCreateLoader():针对指定的 ID 进行实例化并返回新的 Loader

(2)onLoadFinished() :将在先前创建的加载器完成加载时调用

(3)onLoaderReset(): 将在先前创建的加载器重置且其数据因此不可用时调用
(1)onCreateLoader
当您尝试访问加载器时(例如,通过 initLoader()),该方法将检查是否已存在由该 ID 指定的加载器。如果没有,它将触发 LoaderManager.LoaderCallbacks 方法 onCreateLoader()。在此方法中,您可以创建新加载器。 通常,这将是 CursorLoader,但您也可以实现自己的 Loader 子类。

在此示例中,onCreateLoader() 回调方法创建了 CursorLoader。您必须使用其构造函数方法来构建 CursorLoader。该方法需要对 ContentProvider 执行查询时所需的一系列完整信息。具体地说,它需要:

1)uri:用于检索内容的 URI

2)projection:要返回的列的列表。传递 null 时,将返回所有列,这样会导致效率低下

3)selection:一种用于声明要返回哪些行的过滤器,其格式为 SQL WHERE 子句(WHERE 本身除外)。传递 null 时,将为指定的 URI 返回所有行

4)selectionArgs:您可以在 selection 中包含 ?s,它将按照在 selection 中显示的顺序替换为 selectionArgs 中的值。该值将绑定为字串符

5)sortOrder:行的排序依据,其格式为 SQL ORDER BY 子句(ORDER BY 自身除外)。传递 null 时,将使用默认排序顺序(可能并未排序)
例如:
// 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");
}

(2)onLoadFinished
当先前创建的加载器完成加载时,将调用此方法。该方法必须在为此加载器提供的最后一个数据释放之前调用。 此时,您应移除所有使用的旧数据(因为它们很快会被释放),但不要自行释放这些数据,因为这些数据归其加载器所有,其加载器会处理它们。

当加载器发现应用不再使用这些数据时,即会释放它们。 例如,如果数据是来自 CursorLoader 的一个 Cursor,则您不应手动对其调用 close()。如果 Cursor 放置在 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);
}

(3)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);
}

Example(示例)

以下是一个 Fragment 完整实现示例。它展示了一个 ListView,其中包含针对联系人内容提供程序的查询结果。它使用 CursorLoader 管理提供程序的查询。

应用如需访问用户联系人(正如此示例中所示),其清单文件必须包括权限 READ_CONTACTS。

public static class CursorLoaderListFragment extends ListFragmentimplements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {// This is the Adapter being used to display the list's data.SimpleCursorAdapter mAdapter;// If non-null, this is the current filter the user has provided.String mCurFilter;@Override public void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);// Give some text to display if there is no data.  In a real// application this would come from a resource.setEmptyText("No phone numbers");// We have a menu item to show in action bar.setHasOptionsMenu(true);// Create an empty adapter we will use to display the loaded data.mAdapter = new SimpleCursorAdapter(getActivity(),android.R.layout.simple_list_item_2, null,new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },new int[] { android.R.id.text1, android.R.id.text2 }, 0);setListAdapter(mAdapter);// Prepare the loader.  Either re-connect with an existing one,// or start a new one.getLoaderManager().initLoader(0, null, this);}@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {// Place an action bar item for searching.MenuItem item = menu.add("Search");item.setIcon(android.R.drawable.ic_menu_search);item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);SearchView sv = new SearchView(getActivity());sv.setOnQueryTextListener(this);item.setActionView(sv);}public boolean onQueryTextChange(String newText) {// Called when 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;}@Override public boolean onQueryTextSubmit(String query) {// Don't care about this.return true;}@Override public void onListItemClick(ListView l, View v, int position, long id) {// Insert desired behavior here.Log.i("FragmentComplexList", "Item clicked: " + id);}// These are the Contacts rows that we will retrieve.static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {Contacts._ID,Contacts.DISPLAY_NAME,Contacts.CONTACT_STATUS,Contacts.CONTACT_PRESENCE,Contacts.PHOTO_ID,Contacts.LOOKUP_KEY,};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");}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);}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);}
}
如需了解Fragment和Activity详解,请阅读:Android Fragmnet详解、 Android Activity基础详解。

本文是来自于Google Android Developer。

Android Loader(加载器)详解相关推荐

  1. C 编译器、链接器、加载器详解

    原文请见 C 编译器.链接器.加载器详解 0. 预编译 在编译 C++ 程序的预处理阶段,源程序中的所有常量表达式都需要首先计算并替换为对应的具体数值. C语言编译器在对源代码编译之前,还需要进一步的 ...

  2. JVM--类加载器详解

    42. JVM--类加载器详解 ● 类加载器子系统作用: 1. 类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识. 2. ClassLoader只负责 ...

  3. JVM-类加载器 详解(手画多图)面试常问 绝对值得阅读!!!

    受多种情况的影响,又开始看JVM 方面的知识. 1.Java 实在过于内卷,没法不往深了学. 2.面试题问的多,被迫学习. 3.纯粹的好奇. 很喜欢一句话:"八小时内谋生活,八小时外谋发展. ...

  4. JVM------类加载器详解

    JVM------类加载器详解 1.图解类加载器工作流程 2.类加载器种类 3.类加载器的加载顺序 4.一些需要了解的机制 1.图解类加载器工作流程 2.类加载器种类 启动类加载器(Bootstrap ...

  5. C编译器、链接器、加载器详解

    一.概述 C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接.编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程.链接是把目标 ...

  6. android 图片横竖判断_Android横竖屏切换及其对应布局加载问题详解

    本文为大家分享了Android横竖屏切换及其对应布局加载问题,供大家参考,具体内容如下 第一,横竖屏切换连带横竖屏布局问题: 如果要让软件在横竖屏之间切换,由于横竖屏的高宽会发生转换,有可能会要求不同 ...

  7. 【胖虎的逆向之路】02——Android整体加壳原理详解实现

    [胖虎的逆向之路](02)--Android整体加壳原理详解&实现 Android Apk的加壳原理流程及详解 文章目录 [胖虎的逆向之路](02)--Android整体加壳原理详解& ...

  8. php自动加载类与路由,PHP实现路由与类自动加载步骤详解

    这次给大家带来PHP实现路由与类自动加载步骤详解,PHP实现路由与类自动加载步骤详解的注意事项有哪些,下面就是实战案例,一起来看一下. 项目目录如下 入口文件index.php<?php def ...

  9. Java类的加载过程详解 面试高频!!!值得收藏!!!

    受多种情况的影响,又开始看JVM 方面的知识. 1.Java 实在过于内卷,没法不往深了学. 2.面试题问的多,被迫学习. 3.纯粹的好奇. 很喜欢一句话: 八小时内谋生活,八小时外谋发展. 望别日与 ...

  10. 中yeti不能加载_第二十章_类的加载过程详解

    类的加载过程详解 概述 在 Java 中数据类型分为基本数据类型和引用数据类型.基本数据类型由虚拟机预先定义,引用数据类型则需要进行类的加载 按照 Java 虚拟机规范,从 Class 文件到加载到内 ...

最新文章

  1. java log4j mysql_java – log4j:MySQL的JDBCAppender错误
  2. TypeError: 'module' object is not callable 原因分析
  3. angular 指令渲染_Angular JS指令有后期渲染回调吗?
  4. 【Java】5.3 成员变量和局部变量
  5. P1064 金明的预算方案(分组背包)
  6. ArcGIS License启动无响应
  7. c语言小数加分,求救:c语言课程设计 员工工资管理程序 有加分的。谢谢
  8. encipher.min.php,陌屿授权系统(5.7)最新版 网站授权 - 下载 - 搜珍网
  9. 【渝粤教育】国家开放大学2019年春季 0736-22T烹饪工艺学(2) 参考试题
  10. 警告warningC4018有符号/无符号不匹配
  11. [vue] watch和计算属性有什么区别?
  12. ServletResponse的getOutputStream()与getWriter()使用冲突
  13. Sublime Text 使用记录汇总
  14. CentOS中文乱码问题
  15. 1.84亿月活换来400亿港元估值,网易云音乐的钱途在哪儿?
  16. Oracle批量导出Hive建表语句
  17. 在香港用什么软件可以唱歌?香港K歌app推荐
  18. Mac —— QuickTime录屏 声音小解决
  19. 怎么彻底删除users下的文件夹_users中的那些文件可以删除。。。。。。。有哪些文件是不能删除呢?...
  20. 用crontab每隔1分钟执行一个命令行脚本

热门文章

  1. 暴雪机器人消防_消防机器人技术在消防中的使用
  2. 机械复试面试问题汇总1
  3. 力士乐比例阀DBEM107X/100YG24K4M
  4. 弄懂gas费,读这篇就够了
  5. 激光切割机机械结构设计
  6. vipx和vipkid有什么区别,哪个比较好啊?
  7. 【TDA2x学习】待整理笔记
  8. 项目实战 | 基于RK3566开发板实现USB摄像头推流(ffmpeg+nginx)
  9. Screenshot 屏幕截图
  10. 百度百科创建词条步骤是怎样的?