AsyncTaskLoader设计原理大揭秘
简介
在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
完成数据加载后会回调LoaderInfo
的onLoadComplete()
方法。
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;
}
外部就是通过调用Loader
的registerListener()
方法将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开发来说是这样),那就从MainActivity
的onCreate()
来看起吧。
@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);
}
继续LoaderManager
的doStart()
方法:
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
进入Loader
的startLoading()
方法看看:
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设计原理大揭秘的更多相关文章
- 诗人般的机器学习,ML工作原理大揭秘
诗人般的机器学习,ML工作原理大揭秘 https://mp.weixin.qq.com/s/7N96aPAM_M6t0rV0yMLKbg 选自arXiv 作者:Cassie Kozyrkov 机器之心 ...
- 不要再被骗了------QQ盗号原理大揭秘
前言 相信大家在懵懂无知的时候都有被盗号的经历吧,QQ胡乱的加好友,突然有个好友传了个文件给你,打开以后发现QQ竟然显示强制下线,然后再也上不去了QAQ,很明显,QQ号被人盗了.最近也是很多小伙伴私信 ...
- Protocol Buffer 序列化原理大揭秘 - 为什么Protocol Buffer性能这么好?
前言 习惯用 Json.XML 数据存储格式的你们,相信大多都没听过Protocol Buffer Protocol Buffer 其实 是 Google出品的一种轻量 & 高效的结构化数据存 ...
- App可视化埋点技术原理大揭秘
一.背景 运营者能够对用户行为进行分析的前提,是对大量数据的掌握.在以往,这个数据通常是由开发者在控件点击.页面等事件中,一行行地编写埋点代码来完成数据收集的.然而传统的操作模式每当升级改版时,开发和 ...
- 谷歌钦定的编程语言Kotlin大揭秘
第一时间关注程序猿(媛)身边的故事 谷歌钦定的编程语言Kotlin大揭秘 语法+高级特性+实现原理:移动开发者升职加薪宝典! 谷歌作为世界级的科技公司巨头,强悍的技术研发与创新能力使其一直是业界的楷模 ...
- 迄今为止最硬核的「Java8时间系统」设计原理与使用方法
为了使本篇文章更容易让读者读懂,我特意写了上一篇<任何人都需要知道的「世界时间系统」构成原理,尤其开发人员>的科普文章.本文才是重点,绝对要读,走起! Java平台时间系统的设计方案 几乎 ...
- kafka入门:简介、使用场景、设计原理、主要配置及集群搭建(转)
问题导读: 1.zookeeper在kafka的作用是什么? 2.kafka中几乎不允许对消息进行"随机读写"的原因是什么? 3.kafka集群consumer和producer状 ...
- html5设计原理(转)
转自: http://www.cn-cuckoo.com/2010/10/21/the-design-of-html5-2151.html 今天我想跟大家谈一谈HTML5的设计.主要分两个方面:一 ...
- 【腾讯Bugly干货分享】iOS黑客技术大揭秘
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/5791da152168f2690e72daa4 “8小时内拼工作,8小时外拼成长 ...
随机推荐
- C# JSON格式数据高级用法
JSON简介 JSON(全称为JavaScript ObjectNotation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集.JSON采用完全独立于语言的文本格式, ...
- php_memcahed 使用方法
用php_memcache.dll 扩展库操作方法 1.下载php_memcache.dll 对应的PHP版本拷贝到PHP目录EXT下 2.在php.ini添加扩展extension=php_memc ...
- apache设置头
Apache 及开启压缩及Header信息隐藏:http://centilinux.blog.51cto.com/1454781/792820
- 如何启动mininet实例上的wireshark图形界面
启动wireshark 要启动mininet实例上的wireshark的图形界面,其实关键点只有两个: 保证宿主机上安装了X11 使用ssh -Y mininet@192.168.56.102 登录进 ...
- Vivado Logic Analyzer的进一步探讨
本文基于Vivado 2014.2,代码基于文章http://blog.chinaaet.com/detail/37239中使用的代码. 这一篇仅讨论在综合后的Netlist中选择信号进行捕获的方法. ...
- JAVA设计模式之 命令模式【Command Pattern】
一.概述 命令模式能够将请求发送者和接收者全然解耦.发送者与接收者之间没有直接引用关系,发送请求的对象仅仅须要知道怎样发送请求,而不必知道怎样完毕请求.核心在于引入了命令类,通过命令类来减少发送者和接 ...
- makefile之命令包&多行变量
define&endef 1. 命令包(canned recipes)&多行变量(muti-line variables) The define directive is follow ...
- /proc/version 的生成过程
/proc/version 的生成过程 通常我们cat /proc/version时,会显示kernel相关的版本.编译等信息 那么问题来了,这些信息是怎么生成的呢? /proc/version文件是 ...
- Python随手记
类属性的本质是变量对象. os.path.abspath(path) 返回绝对路径,如果填入相对路径,默认会在前面加上当前目录,组合成绝对路径. >>> os.path.abspat ...
- HTML5七巧板canvas绘图(复习)
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...