2015-11-11 18:25:34

1. Loader是什么?

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

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

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

 //可以在Activity或者Fragment中调用
getSupportLoaderManager().restartLoader(1, null, new LoaderCallback()); //实现一个LoaderCallbacks
public class RxLoaderCallback<D> implements LoaderManager.LoaderCallbacks<D> {
public RxLoaderCallback(Context context) {
} @Override
public Loader<D> onCreateLoader(int id, Bundle args) {
//创建Loader
return new Loader();
} @Override
public void onLoadFinished(Loader<D> loader, D data) {
//Loader执行完成后,返回的结果数据和执行的loader
} @Override
public void onLoaderReset(Loader<D> loader) {
//Loader被重置,此时Loader的数据处于不可用状态,因此任何使用此Loader数据的引用,都应该重置自己
}
} //重写一个Loader
/**
* Created by David on 15/11/11.
*/
public class RxLoader<D> extends Loader<D> {
public RxLoader(Context context, Observable<D> observable) {
super(context);
} @Override
protected void onStartLoading() {
//你要做的任务
super.onStartLoading();
deliverResult(mData);
}
}

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

3. 进入Loader的框架

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

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

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

     public final void startLoading() {
mStarted = true;
mReset = false;
mAbandoned = false;
onStartLoading();
}

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

     public void deliverResult(D data) {
if (mListener != null) {
mListener.onLoadComplete(this, data);
}
}

而这个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在它代码的最后,添加了如下代码:

     // ------------------------------------------------------------------------
// LOADER SUPPORT
// ------------------------------------------------------------------------ /**
* Return the LoaderManager for this fragment, creating it if needed.
*/
public LoaderManager getSupportLoaderManager() {
if (mLoaderManager != null) {
return mLoaderManager;
}
mCheckedForLoaderManager = true;
mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);
return mLoaderManager;
} LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
if (mAllLoaderManagers == null) {
mAllLoaderManagers = new SimpleArrayMap<String, LoaderManagerImpl>();
}
LoaderManagerImpl lm = mAllLoaderManagers.get(who);
if (lm == null) {
if (create) {
lm = new LoaderManagerImpl(who, this, started);
mAllLoaderManagers.put(who, lm);
}
} else {
lm.updateActivity(this);
}
return lm;
}

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

 public abstract <D> Loader<D> initLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<D> callback); public abstract <D> Loader<D> restartLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<D> callback); public abstract void destroyLoader(int id); public abstract <D> Loader<D> getLoader(int id); public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args); public static void enableDebugLogging(boolean enabled) {
LoaderManagerImpl.DEBUG = enabled;
} 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的实现。

 public class RxLoader<D> extends AsyncTaskLoader<D> {
private D mData; public RxLoader(Context context) {
super(context);
} @Override
public D loadInBackground() {
//要执行的任务mData = DoSomething
return mData;
} @Override
protected void onStartLoading() {
super.onStartLoading();
forceLoad(); //必须调用,否则loadInBackground()不会被执行
}
}

可以看出,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. spring mvc文件上传

    package com.haier.controller.newuser; import com.haier.commons.entity.Response; import com.haier.com ...

  2. web前端之HTML中元素的区分

    作为前端人员,我们就是要与各种超文本标记打交道,用到各种不同的标签元素.在使用的时候不知道有没有注意到他们的分类归属?现在就来说一说博主的见解: 目前博主总结了三种分类方法:一是按封闭来划分,一是按显 ...

  3. 使用PHP的正则抓取页面中的网址

    最近有一个任务,从页面中抓取页面中所有的链接,当然使用PHP正则表达式是最方便的办法.要写出正则表达式,就要先总结出模式,那么页面中的链接会有几种形式呢?   链接也就是超级链接,是从一个元素(文字. ...

  4. 如何用photoshop把图片白色背景变成透明?

    1.当提示配置文件丢失时,选择“指定RGB模式”. 2.Ctrl+J是一个复合动作,复制+新建,会得到一个新图层. 3.选中新图层,点击鼠标左边工具条里面的魔棒工具,再用鼠标点击下图片的某处白色部分, ...

  5. 安装配置dradis

    github:https://github.com/dradis/dradis-ce/blob/master/README.md安装出现错误:== Copying sample files == == ...

  6. Virtualbox中不能为虚拟机打开一个新任务的原因及解决方法

    VirtualBox新建虚拟机时报错,不能为虚拟机打开一个新任务的原因 解决办法如下 1.保证bios里的virtualization technology的选项开启,不同电脑BIOS设置可能会不一样 ...

  7. 实验二 用C语言表示进程的调度

    实验二 一. 实验目的 通过模拟进程的调度,进一步了解进程的调度的具体过程. 二. 实验内容和要求 1.进程PCB的结构体定义 2.定义队列 3.输入进程序列 4.排序(按到位时间) 5.输出进程运行 ...

  8. Java知识积累1-StringAlign实现文字居中左右对齐

    import java.text.*;public class StringAlign extends Format{ public static final int JUST_LEFT='l'; / ...

  9. PHP的基本排序算法

    1 冒泡排序 <?php /** * 冒泡排序(从小到大) * 基本思想:两两比较相邻记录,一次循环确定一个数 * 在所有的排序算法中是最慢的,尤其当数据量较大的时候,数据小体现不出来 */ $ ...

  10. return 关键字的作用

    注意: 如果一个函数的返回值类型是具体的数据类型,那么该函数就必须要保证在任意情况下都保证有返回值.(除了返回值类型是void以外) return 关键字的作用: 1. 返回数据给函数的调用者. 2. ...