简介

Android异步处理之AsyncTaskLoader简单使用中我简单的介绍了一下AsyncTaskLoader的基本用法和使用场景,对AsyncTaskLoader还不是很熟悉的小伙伴可以先简单学习一下。

相信读过Android异步处理之AsyncTaskLoader简单使用后,大家对烤面包机,面包师,面包房的例子还是有点印象的,那么接下来趁热打铁,继续沿用这个买面包的例子讲述一下AsyncTaskLoader的设计原理。

设计原理

在讲设计原理之前,先简单了解一下AsyncTaskLoader的父类Loader

    A class that performs asynchronous loading of data. While Loaders are active they should monitor the source of their data and deliver new results when the contents change. See LoaderManager for more detail.

简单理解一下Loader就是用来异步加载数据的,当Loader处于活动状态的时候需要监视数据并且在数据发生改变时加载和分发新的数据。在上述描述中我们还发现了LoaderManager这个对象,正是因为有了它,Loader才具有生命力。

下面看一下LoaderManager的简单介绍:

   Interface associated with an Activity or Fragment for managing one or more Loader instances associated with it. This helps an application manage longer-running operations in conjunction with the Activity or Fragment lifecycle; the most common use of this is with a CursorLoader, however applications are free to write their own loaders for loading other types of data. While the LoaderManager API was introduced in HONEYCOMB, a version of the API at is also available for use on older platforms through FragmentActivity. See the blog post Fragments For All for more details.

简单理解一下就是说LoaderManager是配合着Activity,Fragment 的生命周期来管理Loader

接下来用一张类图来简单展示一下Loader,AsyncTaskLoader,AsyncTask,LoaderManager,Activity之间的关系

图-1 相关类之间的关系

接口

1.OnLoadCompleteListener

被声明在Loader中,用于Loader加载完数据后回调,从上图可以看出LoaderInfo实现了这个接口,说明当Loader完成数据加载后会回调LoaderInfoonLoadComplete()方法。

2.LoaderCallbacks

被声明在LoaderManager中,从上图的LoaderInfo中可以看到 mCallbacks这个变量,它便是LoaderCallbacks的引用,用于当Loader加载完数据后回调上面提及的onLoadComplete(),最终回调onLoadFinished()方法将最新加载的数据传递给客户端。

1.Loader

抽象类负责定义相关接口和约束。其变量mListener就是加载完数据的回调。那具体是如何回调的呢?答案就在deliverResult()方法中

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

再看registerListener()方法:

Loader.java
--------------------------
public void registerListener(int id, OnLoadCompleteListener<D> listener) {
if (mListener != null) {
throw new IllegalStateException("There is already a listener registered");
}
mListener = listener;
mId = id;
}

外部就是通过调用LoaderregisterListener()方法将OnLoadCompleteListener接口注册进来的。

2.AsyncTaskLoader

继承自Loader,其中变量mTask正是AsyncTask类型,这里也论证了Android异步处理之AsyncTaskLoader简单使用中的说法,将AsyncTaskLoader比作面包师的话AsyncTask就是烤面包机的说法。AsyncTaskLoader中就是通过AsyncTask来完成异步加载数据这个操作的。

3.LoaderInfo

LoaderInfo其实是对Loader的一个封装,它掌握了Loader一系列的工作状态如:

LoaderInfo.java
-----------------------------
boolean mHaveData;
boolean mDeliveredData;
Object mData;
boolean mStarted;
boolean mRetaining;
boolean mRetainingStarted;
boolean mReportNextStart;
boolean mDestroyed;
boolean mListenerRegistered;

还有一系列的动作指令:

LoaderInfo.java
-----------------------------
void start() {...}
void retain() {...}
void reportStart() {...}
void stop() {...}
void cancel() {...}
void destroy() {...}

4.LoaderManager和LoaderManagerImpl

LoaderManager定义了作为Loader的管理者应该有哪些操作,而LoaderManagerImpl则具体实现这些操作。如果说把Loader比作面包师的话,那LoaderManager就算是面包店的老板吧,厨师什么时候该上班,什么时候该下班都由他管。

其中mLoaders变量为一个数组,用于保存 多个Loader这也说明了一个面包店可以有多个面包师负责制作不同类型的面包如:



这么多种类的面包如果让一个面包师来做我看他也会累的够呛。

运行流程梳理

在接下来的几步中有任何的疑惑都可以回过头看看【图1】。

1

那么了解上述这些类是干嘛的以后我们就来看看当这些个类运行起来是一个怎样的流程吧。

我们还是接着Android异步处理之AsyncTaskLoader简单使用中的例子来讲。一切起源起于onCreate()(至少对于APP开发来说是这样),那就从MainActivityonCreate()来看起吧。

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//这里假设面包房刚开门的时候已经有9个人在排队了。
mNeededBreads = 9;
mBaker = new Baker(this, mBreadCallback);
mBakery = new Bakery(mBaker);
//1.实现`LoaderCallbacks`接口。
mCallbacks = new LoaderCallbacks<List<Bread>>() {
@Override
public Loader<List<Bread>> onCreateLoader(int id, Bundle args) {
if (mBaker == null) {
mBaker = new Baker(MainActivity.this, mBreadCallback);
}
return mBaker;
} @Override
public void onLoadFinished(Loader<List<Bread>> loader, List<Bread> data) {
mNeededBreads = 0 ;
Log.d("scott", "sell " + data.size() + " breads") ;
} @Override
public void onLoaderReset(Loader<List<Bread>> loader) { }
};
//2.在`LoaderManager`中注册这个接口。
getLoaderManager().restartLoader(mLoaderId, null, mCallbacks);
//3.模拟源源不断的顾客
mockCustomer();
}

这一步主要做了三件事情:

1.实现LoaderCallbacks接口。

2.在LoaderManager中注册这个接口。

3.模拟源源不断的顾客

那么这里的mCallbacks充当了什么角色呢?其实它应该相当于一个面包师Loader和面包房店长LoaderManager的中间桥梁。当店长需要面包师的时候就会调用onCreateLoader()来获得一个面包师。同样当面包师完成面包的烤制工作后就会调用onLoadFinished()来告诉店长面包做好了。但实际情况应该不会如此,面包做好了服务员应该会直接将面包传递给顾客。

2

接下来我们看一下restartLoader()这个方法:

LoaderManager.java
--------------------------------------------------------------
public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
LoaderInfo info = mLoaders.get(id);
//...省略部分代码
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
return (Loader<D>)info.mLoader;
}

这里直接调用了createAndInstallLoader()方法来生成一个LoaderInfo对象。接着看createAndInstallLoader()方法:

LoaderManager.java
--------------------------------------------------------------
private LoaderInfo createAndInstallLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
try {
mCreatingLoader = true;
//1.创建LoaderInfo对象
LoaderInfo info = createLoader(id, args, callback);
//2.安装LoaderInfo对象
installLoader(info);
return info;
} finally {
mCreatingLoader = false;
}
}

3

这里分两步来看:

1.创建LoaderInfo对象

LoaderInfo.java
------------------------------------------------------------
private LoaderInfo createLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
//实例化LoaderInfo,并将id,args,callback赋值给mId,mArgs,mCallbacks
LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
//这里的callback就是上面onCreate中的mCallbacks
//获得Loader实例Baker
Loader<Object> loader = callback.onCreateLoader(id, args);
//将Baker赋值给info中的mLoader字段
info.mLoader = (Loader<Object>)loader;
return info;
}

2.安装LoaderInfo对象

LoaderInfo.java
------------------------------------------------------------
void installLoader(LoaderInfo info) {
//将info放入mLoaders数组
mLoaders.put(info.mId, info);
//这一步mStarted=false,不会走下面的if条件语句,那么到这里一切都结束了?
if (mStarted) {
// The activity will start all existing loaders in it's onStart(),
// so only start them here if we're past that point of the activitiy's
// life cycle
info.start();
}
}

4

到这里其实我们已经不能在往下跟代码了,因为此时的mStarted=false,也就是说不会走info.start()这个方法。那么数据是在什么时候被加载的呢?冷静看上面的这段英文注释,Activity会在它的onStart()方法中启动所有已经存在的Loader,真是山穷水尽疑无路,柳暗花明又一村。我们就去onStart()中看个究竟。

Activity.java
------------------------------------------------------
protected void onStart() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this);
mCalled = true; if (!mLoadersStarted) {
mLoadersStarted = true;
if (mLoaderManager != null) {
//看这里o(^▽^)o
mLoaderManager.doStart();
} else if (!mCheckedForLoaderManager) {
mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);
}
mCheckedForLoaderManager = true;
} getApplication().dispatchActivityStarted(this);
}

继续LoaderManagerdoStart()方法:

LoaderManager.java
--------------------------------------------------------------
void doStart() {
if (DEBUG) Log.v(TAG, "Starting in " + this);
if (mStarted) {
RuntimeException e = new RuntimeException("here");
e.fillInStackTrace();
Log.w(TAG, "Called doStart when already started: " + this, e);
return;
} mStarted = true; // Call out to sub classes so they can start their loaders
// Let the existing loaders know that we want to be notified when a load is complete
//看,在这里LoaderInfo被启动了
for (int i = mLoaders.size()-1; i >= 0; i--) {
mLoaders.valueAt(i).start();
}
}

5

下面转移战场进入LoaderInfo看看


LoaderInfo.java
--------------------------------------------
void start() { //...
mStarted = true;
if (mLoader == null && mCallbacks != null) {
mLoader = mCallbacks.onCreateLoader(mId, mArgs);
}
if (mLoader != null) {
//...
if (!mListenerRegistered) {
//将OnLoadCompleteListener接口注册给Loader
mLoader.registerListener(mId, this);
//将OnLoadCanceledListener接口注册给Loader
mLoader.registerOnLoadCanceledListener(this);
mListenerRegistered = true;
}
//开始加载,实质性的一步。
mLoader.startLoading();
}
}

6

进入LoaderstartLoading()方法看看:

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

7

接着看onStartLoading():

Loader.java
--------------------------------------------
/**
* Subclasses must implement this to take care of loading their data,
* as per {@link #startLoading()}. This is not called by clients directly,
* but as a result of a call to {@link #startLoading()}.
*/
protected void onStartLoading() {
}

注释写的很清楚,子类必须要覆盖这个方法,接着我们看看我们久违的Baker(比忘了Baker可是Loader的子类啊)吧:

Baker.java
--------------------------------------------
@Override
protected void onStartLoading() {
//这个可以解释为强行加载,太暴力了。
forceLoad();
}

8

那么forceLoad()又是哪里的呢?

Loader.java
--------------------------------------------
/**
* Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
* loaded data set and load a new one. This simply calls through to the
* implementation's {@link #onForceLoad()}. You generally should only call this
* when the loader is started -- that is, {@link #isStarted()} returns true.
*
* <p>Must be called from the process's main thread.
*/
public void forceLoad() {
onForceLoad();
}

接着看onForceLoad():

Loader.java
--------------------------------------------
/**
* Subclasses must implement this to take care of requests to {@link #forceLoad()}.
* This will always be called from the process's main thread.
*/
protected void onForceLoad() {
}

9

又来这套。。。服了Google的工程师了。接着在AsyncTaskLoader中找到了onForceLoad()

AsyncTaskLoader.java
--------------------------------------------
@Override
protected void onForceLoad() {
super.onForceLoad();
cancelLoad();
//这里的LoadTask继承自AsyncTask
mTask = new LoadTask();
if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
//执行准备就绪的mTask
executePendingTask();
}

接着看executePendingTask():

AsyncTaskLoader.java
--------------------------------------------
void executePendingTask() {
if (mCancellingTask == null && mTask != null) {
//...
//...
//到这里mTask就真正被执行了,即烤面包机考试工作了。
mTask.executeOnExecutor(mExecutor, (Void[]) null);
}
}

10

接下来我们来看一下mTask对应的类LoadTask的定义吧。

 final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {
private final CountDownLatch mDone = new CountDownLatch(1); // Set to true to indicate that the task has been posted to a handler for
// execution at a later time. Used to throttle updates.
boolean waiting; /* Runs on a worker thread */
@Override
protected D doInBackground(Void... params) {
if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
try { D data = AsyncTaskLoader.this.onLoadInBackground();
if (DEBUG) Log.v(TAG, this + " <<< doInBackground");
return data;
} catch (OperationCanceledException ex) {
//...
if (DEBUG) Log.v(TAG, this + " <<< doInBackground (was canceled)", ex);
return null;
}
} /* Runs on the UI thread */
@Override
protected void onPostExecute(D data) {
if (DEBUG) Log.v(TAG, this + " onPostExecute");
try {
AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
} finally {
mDone.countDown();
}
} /* Runs on the UI thread */
@Override
protected void onCancelled(D data) {
//...
} /* Runs on the UI thread, when the waiting task is posted to a handler.
* This method is only executed when task execution was deferred (waiting was true). */
@Override
public void run() {
//...
} /* Used for testing purposes to wait for the task to complete. */
public void waitForLoader() {
//...
}
}

11

这里有两个方法需要关注:

1.doInBackground()方法

用过AsyncTask的应该都了解,异步操作都是放在这里执行的,我们看一下都做了什么操作?

D data = AsyncTaskLoader.this.onLoadInBackground();

接着看onLoadInBackground:

 protected D onLoadInBackground() {
return loadInBackground();
}

这个loadInBackground()是不是有点熟悉了?没错这就是我们在Baker中重写的方法:

Baker.java
------------------------------------------------------
@Override
public List<Bread> loadInBackground() {
List<Bread> breads = new ArrayList<Bread>();
int needs = mCallback.getNeededBreads();
for (int i = 0; i < needs; i++) {
breads.add(new Bread());
}
return breads;
}

OK,到这里面包已经烤完(耗时操作),接着就看这些香喷喷的面包怎么到顾客的手里的吧?

12

2.onPostExecute()方法

LoadTask.java
-------------------------------------------------
@Override
protected void onPostExecute(D data) {
if (DEBUG) Log.v(TAG, this + " onPostExecute");
try {
AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
} finally {
mDone.countDown();
}
}

走的是dispatchOnLoadComplete()方法:

AsyncTaskLoader.java
------------------------------------------------------
void dispatchOnLoadComplete(LoadTask task, D data) {
if (mTask != task) {
if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
//容错处理
dispatchOnCancelled(task, data);
} else {
if (isAbandoned()) {
// This cursor has been abandoned; just cancel the new data.
onCanceled(data);
} else {
commitContentChanged();
mLastLoadCompleteTime = SystemClock.uptimeMillis();
mTask = null;
if (DEBUG) Log.v(TAG, "Delivering result");
//重点在这里
deliverResult(data);
}
}
}

13

继续往下走 deliverResult(data):

Loader.java
------------------------------------------------------
public void deliverResult(D data) {
if (mListener != null) {
//这里的mListener就是之前在【5】中注册的OnLoadCompleteListener接口
mListener.onLoadComplete(this, data);
}
}

14

那么自然又要转移到LoaderInfo中的onLoadComplete()中去了:

LoaderInfo.java
------------------------------------------------------
@Override
public void onLoadComplete(Loader<Object> loader, Object data) {
//... if (mLoaders.get(mId) != this) {
// This data is not coming from the current active loader.
// We don't care about it.
if (DEBUG) Log.v(TAG, " Ignoring load complete -- not active");
return;
} LoaderInfo pending = mPendingLoader;
if (pending != null) {
// There is a new request pending and we were just
// waiting for the old one to complete before starting
// it. So now it is time, switch over to the new loader.
//...
return;
} // Notify of the new data so the app can switch out the old data before
// we try to destroy it.
if (mData != data || !mHaveData) {
mData = data;
mHaveData = true;
if (mStarted) {
//重点看这里
callOnLoadFinished(loader, data);
}
} //if (DEBUG) Log.v(TAG, " onLoadFinished returned: " + this); // We have now given the application the new loader with its
// loaded data, so it should have stopped using the previous
// loader. If there is a previous loader on the inactive list,
// clean it up.
//...
}

15

继续看callOnLoadFinished()

LoaderInfo.java
------------------------------------------------------
void callOnLoadFinished(Loader<Object> loader, Object data) {
if (mCallbacks != null) {
//...
try {
if (DEBUG) Log.v(TAG, " onLoadFinished in " + loader + ": "
+ loader.dataToString(data));
//这里是重点了
mCallbacks.onLoadFinished(loader, data);
} finally {
//...
}
mDeliveredData = true;
}

mCallbacks又是什么呢?在第【3】步中的:

//实例化LoaderInfo,并将id,args,callback赋值给mId,mArgs,mCallbacks
LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);

而这里的callback就是我们在第【1】步中定义的mCallbacks 对象。

饶了这么大一圈,最后还是走到了第【1】步中的:

 @Override
public void onLoadFinished(Loader<List<Bread>> loader, List<Bread> data) {
mNeededBreads = 0 ;
//此时面包以成功送至顾客手中(相当于将数据更新在UI上,这里是main线程大家大可放心使用这些数据)
Log.d("scott", "sell " + data.size() + " breads") ;
}

那么到此为止这个流程就走完了。

总结

1.Loader可以配合中Activity或Fragment的生命周期来加载数据。

2.读源码的时候画类关系图很重要!读源码的时候画类关系图很重要!读源码的时候画类关系图很重要!

3.文章写的仓促,如果有有问题的地方欢迎指出。

AsyncTaskLoader设计原理大揭秘的更多相关文章

  1. 诗人般的机器学习,ML工作原理大揭秘

    诗人般的机器学习,ML工作原理大揭秘 https://mp.weixin.qq.com/s/7N96aPAM_M6t0rV0yMLKbg 选自arXiv 作者:Cassie Kozyrkov 机器之心 ...

  2. 不要再被骗了------QQ盗号原理大揭秘

    前言 相信大家在懵懂无知的时候都有被盗号的经历吧,QQ胡乱的加好友,突然有个好友传了个文件给你,打开以后发现QQ竟然显示强制下线,然后再也上不去了QAQ,很明显,QQ号被人盗了.最近也是很多小伙伴私信 ...

  3. Protocol Buffer 序列化原理大揭秘 - 为什么Protocol Buffer性能这么好?

    前言 习惯用 Json.XML 数据存储格式的你们,相信大多都没听过Protocol Buffer Protocol Buffer 其实 是 Google出品的一种轻量 & 高效的结构化数据存 ...

  4. App可视化埋点技术原理大揭秘

    一.背景 运营者能够对用户行为进行分析的前提,是对大量数据的掌握.在以往,这个数据通常是由开发者在控件点击.页面等事件中,一行行地编写埋点代码来完成数据收集的.然而传统的操作模式每当升级改版时,开发和 ...

  5. 谷歌钦定的编程语言Kotlin大揭秘

    第一时间关注程序猿(媛)身边的故事 谷歌钦定的编程语言Kotlin大揭秘 语法+高级特性+实现原理:移动开发者升职加薪宝典! 谷歌作为世界级的科技公司巨头,强悍的技术研发与创新能力使其一直是业界的楷模 ...

  6. 迄今为止最硬核的「Java8时间系统」设计原理与使用方法

    为了使本篇文章更容易让读者读懂,我特意写了上一篇<任何人都需要知道的「世界时间系统」构成原理,尤其开发人员>的科普文章.本文才是重点,绝对要读,走起! Java平台时间系统的设计方案 几乎 ...

  7. kafka入门:简介、使用场景、设计原理、主要配置及集群搭建(转)

    问题导读: 1.zookeeper在kafka的作用是什么? 2.kafka中几乎不允许对消息进行"随机读写"的原因是什么? 3.kafka集群consumer和producer状 ...

  8. html5设计原理(转)

    转自:   http://www.cn-cuckoo.com/2010/10/21/the-design-of-html5-2151.html 今天我想跟大家谈一谈HTML5的设计.主要分两个方面:一 ...

  9. 【腾讯Bugly干货分享】iOS黑客技术大揭秘

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/5791da152168f2690e72daa4 “8小时内拼工作,8小时外拼成长 ...

随机推荐

  1. Oracle 性能调优案例(代码级别)

    业务案例一: 业务:千万记录表中查询出50条符合条件的记录. 现象:oracle部署时跨机器,业务取得数据耗时10ms.造成业务性能不达标. 为了突出主题,对于异常分支,均已省略. 对于通常写法, o ...

  2. jQuery改变label/input的值,改变class,改变img的src

    jQuery改变label/input的值.改变class,改变img的src jQuery改变label的值: $('#aID').text("New Value"); jQue ...

  3. NOJ 1012 进制转换(十进制转换成随意进制)

    题目: 进制转换 时间限制(普通/Java) : 1000 MS/ 3000 MS          执行内存限制 : 65536 KByte总提交 : 1819            測试通过 : ...

  4. NGUI中获取鼠标在控件内部坐标

    在UIWidget 中添加以下函数.获得的坐标系是以右上角为原点坐标,x轴向左,一轴向下. public Vector2 GetTouchPoint() { Vector3 p0 =  cachedT ...

  5. Tomcat启动时报 java.lang.OutOfMemoryError: Java heap space

    见效的解决方法如下:   在myeclipse中修改jvm启动的参数 打开Myeclipse -->windows-->preference-->myeclipse->serv ...

  6. linux 从百度网盘下载文件的方法

    linux 从百度网盘下载文件的方法 发表于2015 年 月 日由shenwang 方法1.wget wget是在Linux下开发的开放源代码的软件,作者是Hrvoje Niksic,后来被移植到包括 ...

  7. php用explode,可以提供多个字符作为分割符来进行分割数组吗?

    explode — 使用一个字符串分割另一个字符串, 它的函数原型如下: array explode ( string $delimiter , string $string [, int $limi ...

  8. Window 窗口类

    窗口类 WNDCLASS 总结 总结为下面的几个问题: . 什么是窗口类 . 窗口类的三种类型 . 窗口类各字段含义 . 窗口类的注册和注销 . 如何使用窗口类,子类化.超类化是什么 下面分别描述: ...

  9. 去掉IntelliJ IDEA代码编辑区域的竖线

    (网络配图) 作为从事编程或者测试工作的人来说,尤其是有强迫症的,看着非常痛苦,我们来看看怎么去掉 在 Settings-> Editor-> General-> Appearanc ...

  10. javascript的弹框

    学习js最先了解到的两种种简单测试手段就是alert("blah");和console.log("blah");了. 除了alert之外,js还有两种弹框 co ...