Android中的Loaders机制

2023-11-12

转自:http://blog.csdn.net/guoshaobei/article/details/17451647


Loaders机制在Android 3.0版本后引入。Loaders机制使一个Activity或者一个Fragment更加容易异步加载数据。Loaders有如下的特性:

Ø  它们适用于任何Activity和Fragment;

Ø  它们提供了异步加载数据的机制;

Ø  它们检测数据源,当数据源内容改变时它们能够传递新的结果;

Ø  当配置改变后需要重新创建时,它们会重新连接到最后一个loader的游标。这样,它们不需要重新查询它们的数据。


1 Loader API 总结

在app里可以使用与loaders相关的很多的类和接口。总结如下:

Class/Interface

描述

LoaderManager

一个与Activity和Fragment有关联的抽象类,用于管理一个或多个Loader实例。这有助于app管理长运行操作。使用它的最显著的例子是CursorLoader。每个Activity或Fragment只能有一个LoaderManager。而一个LoaderManager可以有多个loaders。

LoaderManager.LoaderCallbacks

提供给客户端的一个callback接口,用于和LoaderManager进行交互。例如,你可以使用onCreateLoader() callback来创建一个新的loader。

AsyncTaskLoader

一个抽象Loader,提供一个AsyncTask进行工作。

CursorLoader

AsyncTaskLoader的子类,用于向ContentResover请求,返回一个Cursor。这个类以标准的游标查询方式实现了Loader协议,建立了AsyncTaskLoader,使用一个后台线程来进行游标查询,不会阻塞app的UI。因此,使用这个loader是从ContentProvider加载异步数据的最好的方式。

上述的class和interface是你在app里实现一个loader所需要的组件。你不必使用所有的组件,但是你通常需要一个LoaderManager的引用(用于初始化一个loader)和一个Loader实现类(例如CursorLoader)。


2 在APP里使用Loaders

一个App里,典型的使用loaders包含的内容如下:

一个Activity或一个Fragment。

一个LoaderManager的实例。

一个CursorLoader,从一个ContentProvider里加载数据。

一个LoaderManager.LoaderCallbacks的实现。在这你创建新的loader,和管理已经存在的loaders。

一种显示loader加载数据的方式,例如SimpleCursorAdapter。

一种数据源,例如一个Conterprovider(当使用CursorLoader)。


2.1 启动一个Loader

在一个Activity或Fragment里,LoaderManager管理一个或多个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()方法有三个参数:

Ø  一个标志loader的ID。

Ø  提供给loader构造函数的参数,可选。

Ø  一个LoaderManager.LoaderCallbacks的实现。

 

initLoader()的调用确保了一个loader被初始化和激活。它有两种结果:

如果标志loader的ID已经存在,则最后创建的loader被复用。

如果标志loader的ID不存在,initLoader()会激发LoaderManager.LoaderCallbacks的方法onCreateLoader()。

在这两种情形下,给定的LoaderManager.LoaderCallbacks实例被关联到loader,并且当loader状态变化时被调用。如果调用者正处于其开始状态并且被请求的loader已经存在,且已产生了数据,那么系统立即调用onLoadFinished()(在initLoader()调用期间),所以你必须准备好这种情况的发生。

 

记住,intiLoader()会返回一个创建的loader,但是你不需要来获取它的引用。LoadeManager会自动管理loader的生命周期。LoaderManager会开始loading,结束loading,维护loader的状态,以及相关的内容。这意味着,你几乎不用直接和loaders进行交互。当有特定事件发生时,你仅仅需要使用LoaderManager.LoaderCallbacks方法来干预loading的过程。


2.2 重启一个loader

当你调用一个initLoader(),如上述所示,你会得到一个ID已经存在的loader,或者创建一个新的loader。但是有时候,你想丢弃掉你的旧数据,重新开始。

要丢弃掉你的旧数据,你要调用restartLoader()。例如,SearchView.OnQueryTextListener的实现重启了loader,当用户的查询发生变化时。loader需要重启,是由于它要使用修改过的搜索过滤器来进行新的查询:

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


2.3 使用LoaderManagerCallbacks

LoaderManager.LoaderCallbacks是callback接口,给client提供与LoaderManager交互的接口。

Loaders,特别是CursorLoader,在被停止后期望能够维持它们的数据。这允许apps在actvitiy或fragemnt的onStop()和onStart()方法里能够保持它们的数据,它们不需要等待数据重新被加载。你使用LoaderManager.LoaderCallbacks方法,知道什么时候该创建一个新的loader,告诉apps什么时候该停止使用一个loader的数据。

 

LoaderManager.LoaderCallbacks包含了三个方法:

onCreateLoader()--- 实例化和返回一个新创建的给定ID的loader

onLoadFinished()--- 当一个创建好的loader完成了load,调用此函数

onLoaderReset()--- 当一个创建好的loader要被reset,调用此函数,这样导致它的数据无效


2.3.1 onCreateLoader

当你尝试访问一个loader(例如,通过initLoader()),它会检查给定的loader的ID是否存在。如果不存在,它会触发LoaderManager.LoaderCallbacks里的方法onCreateLoader(),来创建一个新的loader。典型的例子是CursorLoader。

在这个例子里,onCreateLoader()回调函数创建一个CursorLoader。你必须使用CursorLoader的构造函数,它需要一些额外的信息用于查询一个ContentProvider。它需要:

uri--- 取得内容的URI。

projection--- 要返回的列的list。传递null则返回所有的列,这种做法不够高效。

selectionArgs--- 你也许要在selection里包含 ?s,被selectionArgs里的值替换。

sortOrder--- 行的排序由SQL 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.3.2 onLoadFinished

当一个创建好的loader结束了它的load,此方法被调用。这个方法确保在释放loader维持的数据之前调用。在这个点上,你应当移除所有对旧数据的使用(因为旧数据不久就要被释放),不用释放旧数据,loader会完成旧数据的释放。

 

loader一旦知道app不再使用它,它就会释放掉数据。例如,如果数据是来自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);
  }

2.3.3onLoaderReset

当建立好的loader正在被重启时,此方法被调用,这样让loader的数据置于无效状态。这个回调函数让你发现什么时候数据要被释放掉,在这个点上你可要移除对它的引用。

例如:

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


3 样例

这个样例是一个Fragment的实现,它使用ListView显示了通讯录查询的结果,使用CursorLoader来管理通讯录Provider的查询。app若需要访问通讯录,你需要在mainfest里添加权限READ_CONTACTS。

public static class CursorLoaderListFragment extends ListFragment
          implements 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);
      }
  }

 

更多的样例请参考API Demos里的"LoaderCursor"和"LoaderThrottle"。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android中的Loaders机制 的相关文章

  • rk3399 高可靠OTA升级

    https blog csdn net m0 37631324 article details 106254910
  • AOD相关机制

    AOD的概念 AOD 即A lways O n D isplay 是android一种低功耗的显示模式的一种应用 他能保证屏幕某块区域一直亮 该应用开启时绘制的频率会低于正常的频率 由于AOD现实的不是和正常的亮屏之后显示的一样 只 会显示
  • Android四种Activity的加载模式

    建议首先阅读下面两篇文章 这样才可以更好的理解Activity的加载模式 Android的进程 线程模型 http www cnblogs com ghj1976 archive 2011 04 28 2031586 html 其中对 An
  • 从源码出发浅析 Android TV 的焦点移动原理(下篇)

    转自 https cloud tencent com developer article 1006297 2 2 findNextFocus 如果开发者没有指定nextFocusId 则用findNextFocus找指定方向上最近的视图看一
  • 深入PMS源码(三)—— PMS中intent-filter的匹配架构

    1 简介 由前面深入PMS源码 一 PMS的启动过程和执行流程和深入PMS源码 二 APK的安装和卸载源码分析两篇文章知道 无论是Android系统启动后执行的PMS启动 还是使用PackageInstaller安装APK的过程 最终都会使
  • Android混合开发全解析

    现在的app都开始流行混合开发了 这是一个app开发的新技术 作为android程序猿的我们也应该要了解并且掌握他 那么在使用之前 我们一定要搞清楚 我们的哪些场景使用混合开发好一些呢 这个问题一定要搞清楚 因为现在的混合开发还不成熟 We
  • Android中添加CallStack打印

    Android 打印调用堆栈 内核 添加头文件 include
  • Android zygote进程启动过程

    zygote启动过程中涉及到以下模块 app process zygote USAP socket FileDescriptor FD AndroidRuntime AppRuntime 定义于app process模块 继承自Androi
  • Android 13 - binder阅读(5)- 使用ServiceManager注册服务2

    上一篇笔记我们看到了binder transaction 这个方法很长 这一篇我们将把这个方法拆分开来看binder transaction做了什么 从而学习binder是如何跨进程通信的 1 binder transaction stat
  • Android图形显示系统6 图像缓冲区(下)

    一 概述 我们再次回顾下上一篇文章 Android图形显示系统5 图像缓冲区 上 描述的图像缓冲区 Android 图形缓冲区由哪些部分组成 Android 的图形缓冲区由 Surface BufferQueue Layer Graphic
  • 如何dump SKP,SKP抓取

    1 如何dump SKP 我们知道绘制的操作 主要都是在SkiaPipline renderframe中进行的 frameworks base libs hwui pipeline skia SkiaPipeline cpp 429 voi
  • Android Watchdog分析

    初始化 Watchdog作为一个独立的线程在SystemServer进程中被初始化 private void startBootstrapServices NonNull TimingsTraceAndSlog t Start the wa
  • Android中的Loaders机制

    转自 http blog csdn net guoshaobei article details 17451647 Loaders机制在Android 3 0版本后引入 Loaders机制使一个Activity或者一个Fragment更加容
  • 深入AMS源码(二)—— ActivityManagerService对Activity的调度管理

    1 概述 在上一篇深入AMS源码 一 ActivityManagerService的基础知识文章介绍了AMS的基础信息 主要包括AMS中主要的数据结构 主要功能类和属性以及AMS中对生命周期的调度方式 本篇主要从源码的角度分析下AMS对Ac
  • 从setContentView到onResume应用显示过程分析

    之前总体笼统地分析了Acitivity从启动到显示的过程 Activty启动到显示的过程 一 Activty启动到显示的过程 二 发现很多细节没有注意到 后续挑些过程中比较重要的部分重点分析 在上一篇文章分析了一个app从zygote到on
  • adb install安装流程

    把一个编译好的APK通过 include BUILD PREBUILT 预制到系统中 但是启动后一直crash log中显示 dlopen failed cannot locate symbol 02 25 16 18 20 143 126
  • Binder (一) mmap与一次拷贝原理

    Binder机制 跨进程通信IPC 远程过程调用手段RPC 4个角色进行粘合 Client Server Service Manager和Binder驱动程序 整个过程只需要一次拷贝 Binder Driver misc设备 dev bin
  • linkToDeath机制了解和使用

    转自 http www jianshu com p e38f08e34686 在学习Binder和AIDL的过程中遇到的一些有意思的事情 linkToDeath机制 我们先看看官网如何介绍 When working with remote
  • Android图形显示系统4 图像生产者(下)

    一 概述 在上一篇文章 Android图形显示系统2 图像消费者 中 我们详细地讲解了图像消费者 我们已经了解了 Android 中的图像元数据是如何被 SurfaceFlinger HWComposer 或者 OpenGL ES 消费的
  • 从zygote到onCreate应用启动过程分析

    在之前的文章讲了app进程大概的启动过程 https blog csdn net qq 36063677 article details 125638137 这篇文章细致的分析一下app进程从zygote进程fork出来到onCreate等

随机推荐

  • 【Linux之Shell脚本实战】查询邮政编码与对应地区

    Linux之Shell脚本实战 查询邮政编码与对应地区 一 脚本要求 二 检查本地系统环境 1 检查系统版本 2 检查系统内核版本 三 配置脚本注释模板 1 编辑 vimrc 文件 2 检查模板生效情况 四 编辑shell脚本 1 创建脚本
  • 「数据结构」三步搞定表达式中缀转后缀 手算法 通俗易懂 C语言

    表达式中缀转后缀 举个例子 一个式子 5 20 1 3 14 如何把该式子转换成后缀表达式呢 其实就是分三步 按运算符优先级对所有运算符和它的运算数加括号 原本有括号的不用加 把运算符移到对应的括号后 去掉括号 对应的具体实现为 5 20
  • 音视频开发系列-音视频核心知识精讲

    音视频开发系列 音视频核心知识精讲 1 视频为什么会花屏 2 音频为什么容易有杂音 3 音视频进阶需要掌握什么项目 音视频核心知识 为什么会花屏 容易有杂音 进阶需要掌握什么项目 https www bilibili com video B
  • day09:定时器

    目录 总结 1 三种解绑事件 2 事件冒泡 3 阻止事件冒泡 4 事件委托 事件代理 5 事件的三个阶段 一 最大的匿名函数 二 定时器setInterval 三 一起摇摆案例 四 亮起来案例 五 美女时钟效果 六 进一步优化时钟 七 渐变
  • 我们用4行代码节省了100万 相见恨晚的PCDN

    我们公司主要做视频在线点播 还有少量视频下载 比较关心网络加速 首先就是价格 其次是首播时间 流畅率这几个核心性能指标 目前使用阿里云PCDN也有几个月了 整体结果是超预期 值得安利的 写这篇文章 希望能通过选型对比 接入过程 效果实现几个
  • torchserve使用-注册模型设置参数(二)

    目录 1 自定义处理程序 2 托管多个模型 3 模型接口 3 1 添加注册新模型 3 2 查看是否注册成功 3 3 查看注册模型基本信息 3 4 设置注册模型参数 3 5 使用以下代码注销模型 3 6 模型版本控制 4 记录和指标 1 自定
  • xxl-job-admin多数据库支持

    记录一下改造过程 针对 xxl job 2 3版本 什么是xxl job 你的系统中有很多定时任务 如果你想统一管理 你需要一个调度系统 XXL JOB是一个分布式任务调度平台 其核心设计目标是开发迅速 学习简单 轻量级 易扩展 githu
  • 机器学习初实践——恶意域名检测

    这次恶意域名检测实践是第一次自己做机器学习而非单纯复现 参考了第一次鸢尾花的代码和GitHub的UrlDetect中的特征提取参数的代码 一 数据处理 首先要实现自动化处理数据 在这里我没有使用urlparser而是直接写脚本提取域名 提取
  • 【南邮操作系统实验】页面置换算法 (FIFO、LRU、OPTP)

    写在前面 操作系统内存管理的页面置换算法 因为懒得看老师给的代码 太长了而且据说好像还有错误 就自己写了一个python版本的 因为比较菜 所以写的一般般 仅供大伙参考一下Orz python版本的 代码如下 import random 生
  • mysql视图基本操作

    mysql视图介绍及如何创建视图请看 一个案例理解mysql视图 本章主要记录视图的修改 删除以及展示视图语法 目录 一 创建视图 二 修改视图 三 删除视图 四 更新视图 五 展示视图 一 创建视图 请看 一个案例理解mysql视图 二
  • Docker 使用网络

    文章目录 外部访问容器 端口绑定 映射所有接口地址 映射到指定地址的指定端口 查看当前端口配置 多个端口绑定 容器互联 配置 DNS 外部访问容器 1 使用 P 标记时 Docker 会随机映射一个 490000 49900 的端口到内部容
  • 应用Cryptopp库实现AES加密【转】

    crypto 自身的wiki上就有一些例子 可以参考 http www cryptopp com wiki Category Sample 本文来源 http ste xidian edu cn bbs a a asp B 5 ID 224
  • DirectX11学习笔记(不定期更新)

    目录 1 DX与HLSL的矩阵 2 创建常量缓冲区的尺寸需要按照16字节对齐 1 DX与HLSL的矩阵 DX的矩阵 DirectX XMMATRIX 按照行主元优先存储 而HLSL的矩阵默认按照列主元优先存储 解决这个问题的方法有两种 1
  • 【STM32 x ESP8266】连接 MQTT 服务器(报文,附部分源码解析)

    MQTT 协议作为物联网非常重要的传输协议 如何使用它十分重要 如果有不理解的同学可以点击这里学习 这里只是简单介绍一下 同时这里附上MQTT 3 1 1协议中文版 pdf 的链接 对协议底层感兴趣的同学可以下载学习一下 同时下面的实现函数
  • Qt Q_UNUSED使用

    以前经常在程序里定义很多局部变量 如果没有使用 Qt Creator就会报 未引用的局部变量 警告 因为不影响程序 一开始也没管 但随着程序代码变多 警告变动还是有点烦 到今天才知道这个Qt的有个消除这个警告的宏 Q UNUSED int
  • Flink系统架构

    Flink 的运行时架构中 最重要的就是两大组件 作业管理器 JobManger 和任务管理器 TaskManager 对于一个提交执行的作业 JobManager 是真正意义上的 管理者 Master 负责管理调度 所以在不考虑高可用的情
  • 如何将java项目部署到Linux服务器上

    博主之前并没有操作过服务器 以及部署项目 记第一次操作心得 仅供参考 在服务器上已经有了mysql的五个rpm安装包 jdk的rpm tomcat的tar gz 博主找了其他的博文说是需要配置java环境但是博主并没有操作到这一步 安装包是
  • ThinkPad开机停留在boot menu界面、进不了系统的解决方法

    方法一 1 开机点击F1进入到bios界面 2 进入Security Secure Boot Disabled 如果不修改Secure boot选项为Disabled 在光驱引导时可能会出现报错 3 进入Startup UEFI Legac
  • C++57个入门知识点_37 虚函数的直接调用与间接调用(函数的调用分为直接调用和间接调用,间接调用是虚函数所具有的的性质;间接调用:运行期通过查找对象的虚表下标来调用函数的方法)

    前面两篇C 57个入门知识点 35 函数覆盖的概念1 函数覆盖条件 父子类继承关系 函数名 参数列表 返回值 调用约定必须相同 有virtual关键字 函数覆盖 类虚表中成员函数从继承自父类变为自己的 C 57个入门知识点 36 函数覆盖的
  • Android中的Loaders机制

    转自 http blog csdn net guoshaobei article details 17451647 Loaders机制在Android 3 0版本后引入 Loaders机制使一个Activity或者一个Fragment更加容