2015-11-11 18:25:34

1. Loader是什么?

  1. /**
  2. * Static library support version of the framework's {@link android.content.Loader}.
  3. * Used to write apps that run on platforms prior to Android 3.0. When running
  4. * on Android 3.0 or above, this implementation is still used; it does not try
  5. * to switch to the framework's implementation. See the framework SDK
  6. * documentation for a class overview.
  7. */

Loader是2011年Android3.0以后引入的一种加载数据的方式,至于是异步还是同步,这取决于你怎么实现。

2. Loader其实是一种框架,它本身什么功能都没有,但是你能很方便的利用这个框架搭建自己的代码,而且搭建好之后,怎么运行,你也不用关心,等待结果就OK了。那么Loader是一种怎样的框架呢?我们先来搞清楚他是怎么用的,然后再去看他的框架。

  1. //可以在Activity或者Fragment中调用
  2. getSupportLoaderManager().restartLoader(1, null, new LoaderCallback());
  3.  
  4. //实现一个LoaderCallbacks
  5. public class RxLoaderCallback<D> implements LoaderManager.LoaderCallbacks<D> {
  6. public RxLoaderCallback(Context context) {
  7. }
  8.  
  9. @Override
  10. public Loader<D> onCreateLoader(int id, Bundle args) {
  11. //创建Loader
  12. return new Loader();
  13. }
  14.  
  15. @Override
  16. public void onLoadFinished(Loader<D> loader, D data) {
  17. //Loader执行完成后,返回的结果数据和执行的loader
  18. }
  19.  
  20. @Override
  21. public void onLoaderReset(Loader<D> loader) {
  22. //Loader被重置,此时Loader的数据处于不可用状态,因此任何使用此Loader数据的引用,都应该重置自己
  23. }
  24. }
  25.  
  26. //重写一个Loader
  27. /**
  28. * Created by David on 15/11/11.
  29. */
  30. public class RxLoader<D> extends Loader<D> {
  31. public RxLoader(Context context, Observable<D> observable) {
  32. super(context);
  33. }
  34.  
  35. @Override
  36. protected void onStartLoading() {
  37. //你要做的任务
  38. super.onStartLoading();
  39. deliverResult(mData);
  40. }
  41. }

这是一个简单的使用,这个框架最核心的东西就是onStartLoading()方法,这里就是你要执行的任务,任务执行结束后,记得调用deliverResult(mData),将结果抛出去。

3. 进入Loader的框架

3.1 读Loader源码,发现实现这套机制最核心的有四个方法:

  1. startLoading() //不需要你调用,是用来启动这个Loader的
  2. onStartLoading() //前面说到了
  3. deliverResult(D data) //抛出结果
  4. registerListener(int id, OnLoadCompleteListener<D> listener) //也不需要你调用,注册一个Listener,执行完成后,抛出结果

事实上,这两个不需要我们来调用的方法是由LoaderManager来调用的,也就是说,当你使用initLoader或者restartLoader的时候被调用的。源码如下:

  1. public final void startLoading() {
  2. mStarted = true;
  3. mReset = false;
  4. mAbandoned = false;
  5. onStartLoading();
  6. }

本质上还是调用了我们实现的onStartLoading()方法。那么,执行完成后的结果是怎么抛出来的呢?这就是deliverResult(D data)干的事情了,源码:

  1. public void deliverResult(D data) {
  2. if (mListener != null) {
  3. mListener.onLoadComplete(this, data);
  4. }
  5. }

而这个mListener就是通过registerListener()方法注册的,同样是由LoaderManager注册,所以LoaderManager中会获得结果,然后调用LoaderCallback的onLoadFinished()方法。但是,有一个问题,按照常规,执行得到结果后应该自动调用mListener.onLoadComplete()方法,但是Loader没有自己调用,必须由我们来触发。有点让人费解,不过我的理解是,Loader框架默认是一个同步的框架,而且这不是一个“很完整”的框架,留下了足够的灵活性,便于我们自己定制。

3.2 总结一下:

     LoaderCallback的作用很明显,就是起到创建Loader和提供回调方法的作用,同时,Loader的onStartLoading()供子类实现,其实这里做成一个抽象方法更好~用来执行真正的任务,同时把结果抛出去。

4. 进入LoaderManager

4.1 我们是通过getSupportLoaderManager().restartLoader(1, null, new LoaderCallback())来使用LoaderManager的,也就是说,是通过getSupportLoaderManager()来获取LoaderManager的,这个方法其实是FragmentActivity提供的,FragmentActivity继承自Activity,只出现在support v4包中,所以只有向下兼容1.6的时候,我们自己的Activity需要继承自FragmentActivity,同时使用getSupportLoaderManager(),如果只在3.0以上系统中使用,那么直接使用Activity中的getLoaderManager()就好了~为了避免重名,所以FragmentActivity才会改名为getSupportLoaderManager()的吧。

4.2 我们以support v4包中的LoaderManager为例,探究一下他的实现(一下出现的Loader、LoaderManager和LoaderCallback均是support v4包中的)。FragmentActivity在它代码的最后,添加了如下代码:

  1. // ------------------------------------------------------------------------
  2. // LOADER SUPPORT
  3. // ------------------------------------------------------------------------
  4.  
  5. /**
  6. * Return the LoaderManager for this fragment, creating it if needed.
  7. */
  8. public LoaderManager getSupportLoaderManager() {
  9. if (mLoaderManager != null) {
  10. return mLoaderManager;
  11. }
  12. mCheckedForLoaderManager = true;
  13. mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);
  14. return mLoaderManager;
  15. }
  16.  
  17. LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
  18. if (mAllLoaderManagers == null) {
  19. mAllLoaderManagers = new SimpleArrayMap<String, LoaderManagerImpl>();
  20. }
  21. LoaderManagerImpl lm = mAllLoaderManagers.get(who);
  22. if (lm == null) {
  23. if (create) {
  24. lm = new LoaderManagerImpl(who, this, started);
  25. mAllLoaderManagers.put(who, lm);
  26. }
  27. } else {
  28. lm.updateActivity(this);
  29. }
  30. return lm;
  31. }

当然了,它使用的同样的是v4包中的LoaderManager。事实上LoaderManager是一个抽象类,因此我们通过getSupportLoaderManager获得的其实是他的一个实现类,名字叫LoaderManagerImpl,这两个类的代码写在同一个java文件中。LoaderManager中有如下方法:

  1. public abstract <D> Loader<D> initLoader(int id, Bundle args,
  2. LoaderManager.LoaderCallbacks<D> callback);
  3.  
  4. public abstract <D> Loader<D> restartLoader(int id, Bundle args,
  5. LoaderManager.LoaderCallbacks<D> callback);
  6.  
  7. public abstract void destroyLoader(int id);
  8.  
  9. public abstract <D> Loader<D> getLoader(int id);
  10.  
  11. public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
  12.  
  13. public static void enableDebugLogging(boolean enabled) {
  14. LoaderManagerImpl.DEBUG = enabled;
  15. }
  16.  
  17. public boolean hasRunningLoaders() { return false; }

下面我们着重看一下LoaderManagerImpl类,首先从他的initLoader和restartLoader开始。开始之前,先来解决一个问题,那就是initLoader和restartLoader的区别:

真正的区别在于:
1. initLoader,如果有缓存LoaderCache,那么传入的bundle参数被忽略,同时新传入的LoaderCallbacks会替换缓存LoaderCache的LoaderCallbacks,这种适用于共用Loader,同时不需要新参数的情况,比如Activity Configuration发生变化导致Activity被销毁、重建时。
2. restartLoader,每次都会创建新的Loader,除非有缓存LoaderCache,并且LoaderCache是活着的但是没有执行结果,那么会新创新一个LoaderNew,LoaderNew只有在LoaderCache执行结束时才会被启动,并且LoaderCache被销毁,产生的结果不会被抛出。

4.3 LoaderManager是如何启动Loader,数据是如何返回的?

当我们在调用initLoader()和restartLoader()的时候,会根据需求创建并启动Loader,同时给Loader注册一个OnLoadCompleteListener,如果Loader执行完任务后,调用onLoadComplete,至于触发这个回调,则需要我们自己来做,前面有提到过。当LoaderManager收到回调返回的数据后,再调用LoaderCallbacks的onLoaderFinished()方法。当然在实际代码中,LoaderManagerImpl又封装了一个LoaderInfo,同时有很多对状态处理的代码,但是主要流程就是如此。LoaderManagerImpl中代码如下:

4.4 销毁Loader

LoaderManager提供了destroyLoader(id)方法,根据id来销毁Loader。

5. AsyncTaskLoader

5.1 前面提到了,Loader是一个框架,而且默认是不支持的框架,而在实际开发中,我们需要的多是异步的调用,所以AsyncTaskLoader应用而生了。顾名思义,AsyncTaskLoader是一个异步调用的Loader,使用方式如下:

LoaderCallbacks和之前的例子是一样的,看看Loader的实现。

  1. public class RxLoader<D> extends AsyncTaskLoader<D> {
  2. private D mData;
  3.  
  4. public RxLoader(Context context) {
  5. super(context);
  6. }
  7.  
  8. @Override
  9. public D loadInBackground() {
  10. //要执行的任务mData = DoSomething
  11. return mData;
  12. }
  13.  
  14. @Override
  15. protected void onStartLoading() {
  16. super.onStartLoading();
  17. forceLoad(); //必须调用,否则loadInBackground()不会被执行
  18. }
  19. }

可以看出,loadInBackground()就是我们自己必须实现的(该方式是abstract)、执行任务的地方,任务可以是一个网络请求或者是任何任务。返回结果什么的,都是在LoaderCallbacks中处理的。有一点要注意,在loadInBackground中不需要手动调用deliveryResult()了,只需要将执行结果返回即可。

5.2 进入看看AsyncTaskLoader框架是怎么实现的

从5.1的使用中我们可以发现,需要在onStartLoading中手动调用forceLoader,根据前面的分析,我们知道onStartLoading中应该是要执行的任务,所以需要手动调用forceLoader(),那么forceLoader到底做了些什么?forceLoader()方法在Loader中,主要调用了onForceLoader,因此我们真正要看的是AsyncTaskLoader中的onForceLoader()方法,代码如下:

如上(1)处的代码,如果当前Task已经存在,那么先干掉。(2)处代码,创建了一个LoaderTask,LoaderTask继承自ModernAsyncTask,其本质上是一个AsyncTask,那么为毛不直接用AsyncTask呢?Google的解释是:为了支持AsyncTaskLoader,从AsyncTask中拷贝了有用的代码,是因为要依赖的AsyncTask的一些微妙的行为在旧的平台是不可靠的~而且由于ModernAsyncTask还没不是最终实现,目前不对外公开,只为AsyncTaskLoader而生。明白了吧,AsyncTask在旧平台上不可靠,而AsyncTaskLoader是要兼容到v4的,啦啦啦

(3)处的代码,就是启动这个像AsyncTask的东西了,然后返回数据,至于不熟悉AsyncTask的同学,自己补脑吧。

最后一个问题:结果是怎么传出来的?记得前面说过,需要在onStartLoading中手动调用deliverResult方法,但是继承自AsyncTaskLoader的子类,好像并没有手动调用,why?其实AsyncTask的onPostExcute中已经调用了,就这么回事。

6. 目前团App项目中,结合Retrofit,继承Loader来使用的,思路也很简单。至于为什么不是继承AsyncTaskLoader呢,是因为Retrofit会自己处理异步的操作,所以没必要。

7. 关于Retrofit,请参考:简单研究下Retrofit

简单研究Loader笔记的更多相关文章

  1. SQLSERVER2012 列存储索引的简单研究和测试

    SQLSERVER2012 列存储索引的简单研究和测试 SQLSERVER2012 列存储索引的简单研究和测试 看这篇文章之前可以先看一下下面这两篇文章: 列存储索引 http://www.cnblo ...

  2. 简单脱壳教程笔记(2)---手脱UPX壳(1)

    本笔记是针对ximo早期发的脱壳基础视频教程,整理的笔记. ximo早期发的脱壳基础视频教程 下载地址如下: http://down.52pojie.cn/%E5%90%BE%E7%88%B1%E7% ...

  3. SQLSERVER中的LOB页面简单研究

    SQLSERVER中的LOB页面简单研究 这篇文章和我另一篇文章是相辅相成的,在看<SQLSERVER2012 列存储索引的简单研究和测试>这篇文章之前希望大家先看一下这篇文章o(∩_∩) ...

  4. 关于BLOB/TEXT字段存储设计及性能的简单研究

    简单研究了一下BLOB/TEXT字段对数据库性能的影响,得到一个大概的结论:(未验证) 无论MySQL还是MSSQL,都可以通过把BLOB/TEXT数据存储在行外的方式提高性能 把BLOB/TEXT字 ...

  5. 读《Wireshark网络分析就这么简单》读书笔记

    晚上花了两个多小时看完这本书,记录下一些看书过程中的笔记. 一.问题:A和B 是否能正常通信? 两台服务器A和服务器B的网络配置 A                                  ...

  6. 对jQuery ajax三级级联的简单研究

    最近写程序的时候经常遇到使用ajax获取数据的问题,刚好昨天遇到ajax写三级级联问题,自己写了一个简单的级联.对于服务端获取数据的就不多写了,客户端的ajax发送请求我在这里详细说一下,因为我也没专 ...

  7. 简单研究下Retrofit

    2015-09-24 15:36:26 第一部分: 1. 什么是Retrofit? (点击图片有惊喜) 以上是来自官网的解释,言简意赅,咳咳,我就不翻译了~ 2. 如何使用Retrofit? 2.1 ...

  8. 简单研究Android View绘制三 布局过程

    2015-07-28 17:29:19 这一篇主要看看布局过程 一.布局过程肯定要不可避免的涉及到layout()和onLayout()方法,这两个方法都是定义在View.java中,源码如下: /* ...

  9. OpenGL的简单研究-开端

    一直想要学习的但是没有学习的东西,大学一直在等待这个时间,终于可以闲下来研究一下这个部分的内容了. 计算机图形学,让计算机处理各种图像的东西,里面也存在很多算法和数学知识,很值得研究的一个领域,之前一 ...

随机推荐

  1. sql-按周输出每月的周日期范围

    --日期参数,此处可以建立存储过程,接收月份,计算月开始结束时间或者直接接受开始与结束时间 declare @begDate datetime = '2014-06-01' declare @endD ...

  2. 一文说尽C++赋值运算符重载函数(operator=)

    写在前面: 关于C++的赋值运算符重载函数(operator=),网络以及各种教材上都有很多介绍,但可惜的是,内容大多雷同且不全面.面对这一局面,在下在整合各种资源及融入个人理解的基础上,整理出一篇较 ...

  3. 。Java中的一些小细节

    1.main方法. ------任何一个Java程序都有一个main方法,它是程序的入口. ------当执行  “ java + 类名 “  这个命令时,JVM就会去加载这个类,并且寻找这个类中的m ...

  4. 景区3D指纹验证系统解决方案

    旅游业已成为全球经济中发展势头最强劲和规模最大的产业之一.旅游业在城市经济发展中的产业地位.经济作用逐步增强,旅游业对城市经济的拉动性.社会就业的带动力.以及对文化与环境的促进作用日益显现.指纹门票为 ...

  5. 携程Ctrip DAL的学习 2

    携程Ctrip DAL的学习 2 本人net开发菜鸟,原理请自行看github的官网源码. 我这里是简单的使用(helloworld的水平). 代码生成器的使用 详见 官网使用说明 注意的地方: 1 ...

  6. Maven安装与使用

    1.安装Maven 1)官网下载Maven : http://maven.apache.org/download.cgi,解压下载文件 2)配置环境变量 3)验证是否已经安装成功:打开cmd,输入mv ...

  7. Gbase数据库备份与还原

    备份命令:cd D:\GeneralData\GBase\Server\bin        回车 后 : d :        回车 后: dump.exe -usysdba(u+用户名) -pbj ...

  8. javascript中利用柯里化函数实现bind方法

    柯理化函数思想:一个js预先处理的思想:利用函数执行可以形成一个不销毁的作用域的原理,把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个小函数,以后我们执行的都是小函数,在小函数中把之前预 ...

  9. js 时间格式化 代码

    Date.prototype.Format = function (fmt) { //author: meizz              var o = {                 &quo ...

  10. 03.JavaScript 面向对象精要--理解对象

    JavaScript 面向对象精要--理解对象 尽管JavaScript里有大量内建引用类型,很可能你还是会频繁的创建自己的对象.JavaScript中的对象是动态的. 一.定义属性 当一个属性第1次 ...