简介

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 expdp导入时 提示“ORA-39002: 操作无效 ORA-39070: 无法打开日志文件 ”

    1.导出数据库的时候报错 expdp zz/zz@orcl directory=exp_dp dumpfile=zz_20170520.dump logfile=zz_20170520.log   2 ...

  2. 运行百度语音识别官方iOS demo报错: load offline engine failed: 4001

    运行官方BDVRClientSample这个demo(ios版的),demo可以安到手机上,但是点“识别UI”那个按钮后“授权验证失败”.如果点“语音识别”那个按钮,控制台输出:2015-10-23 ...

  3. 点滴积累【C#】---使用log4net组件记录错误日志(以文本形式记录)

    效果: 描述: 利用log4net组件进行错误日志的记录,log4net记录错误的方式我所了解的有4种,No.1 文本形式记录日志,No.2存储到数据库形式记录日志,No.3控制台控制显示日志,No. ...

  4. Visual Prolog 的 Web 专家系统 (1)

    用Prolog敲代码,感觉舒坦. Prolog的编程范式,抽象程度远高于中.低级别的C语言等.敲代码的源码长度,至少比C语言的节省50%. 并且,Prolog的语法简单,符号选择自然合理,养眼度远高于 ...

  5. Atitit. 软件开发中的管理哲学--一个伟大的事业必然是过程导向为主 过程导向 vs 结果导向

    Atitit. 软件开发中的管理哲学--一个伟大的事业必然是过程导向为主    过程导向 vs 结果导向 1. 一个伟大的事业必然是过程导向为主 1 1.1. 过程的执行情况(有明确的执行手册及标准) ...

  6. [svc]linux常用手头命令-md版-2017年11月12日 12:31:56

    相关代码 curl命令-网站如果3次不是200或301则报警 curl -o /dev/null -s -w "%{http_code}" baidu.com -k/--insec ...

  7. makefile之shell函数

    shell函数不同于除"wildcard"函数之外的其它函数.make可以使用它来和外部通信. 函数功能:函数"shell"所实现的功能和shell中的引用(` ...

  8. 蓝牙(CoreBluetooth)-概述

    蓝牙(CoreBluetooth)-概述 通过此框架可以让你的Mac和iOS应用程序与外部蓝牙设备通信 外部设备: 就是需要通过iOS App控制器的其他设备: 例如:心率检测仪.数字温控器 蓝牙通讯 ...

  9. vue路由使用

    <body> <div id="itany"> <div> <!--使用router-link组件来定义导航,to属性指定链接url--& ...

  10. oracle数据库访问order by不起作用分析

    `SELECT * FROM student ROWNUM <= 1 ORDER BY id ASC`执行结果,返回结果没有排序.使用驱动"System.Data.OracleClie ...