Android 源码解析之AsyncTask
AsyncTask相信大家都不陌生,它是为了简化异步请求、更新UI操作而诞生的。使用它不仅可以完成我们的网络耗时操作,而且还可以在完成耗时操作后直接的更新我们所需要的UI组件。这使得它在android开发中成为炙手可热的网络请求工具类。
而今天我们就以源码分析的形式来彻底的学习下它的实现过程。
首先,我们先看看AsyncTask的定义形式:
public abstract class AsyncTask<Params, Progress, Result> {
}
首先AsyncTask它是一个抽象类,包括三种泛型类型,具体含义如下:
- Params:它表示请求参数的类型
- Progress:执行任务的进度类型
- Result:返回结果的类型
但是以上三个参数并不是一定必须,在不需要时可以设置为Void,没有返回类型。
然后我们看看它的执行过程,包括以下几个方法:
execute(Params... params),我们在执行异步操作时会调用该方法,表示开始执行任务。
protected void onPreExecute() {},在调用execute方法后,该方法就会得到执行,它执行在UI线程中,用来初始化一些UI空间等
protected abstract Result doInBackground(Params... params);在onPreExecute执行完后将会执行该方法,它执行在后台,并接受一个Params类型的数组参数,用于请求网络,并且它返回一个Result 类型的结果。该方法中可以在执行网络请求的同时更新请求进度,调用publishProgress(Progress... values) 。
protected void onProgressUpdate(Progress... values) ,假如在doInBackground方法中调用了publishProgress方法,那么该方法就会得到执行,它是执行在UI线程的,根据values的值不停的更改进度,以达到想要的效果。
protected void onPostExecute(Result result),该方法是在doInBackground方法执行完毕后得到执行,可根据doInBackground返回的结果进行后续的UI操作,由此可见它是工作在UI线程中的。
经过上面一系列的方法运转,一个完整的AysncTask请求就正式的完成了任务。不仅完成了耗时操作还更新的UI组件,这就是它的魅力所在。但是这时候你该有疑问了,上面的方法都是你说执行哪个就执行哪个,哪到底是怎么执行的呢?
那么接下来就正式的揭开它的庐山正面目。
在正式介绍它的源码之前,你必须知道new 一个类它所执行的过程:
在new的过程中,它会首先一层一层的加载它所继承的父类的成员变量及构造方法
然后在加载自己的成员变量和构造方法。
顺序是不可变得。
那么看看在我们执行 new AsyncTask()中,它到底加载了哪些成员呢?
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static InternalHandler sHandler;
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
private volatile Status mStatus = Status.PENDING;
private final AtomicBoolean mCancelled = new AtomicBoolean();
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
private static class SerialExecutor implements Executor{...}
public enum Status {
PENDING,
RUNNING,
FINISHED,
}
看到这么一大堆是不是很麻头皮,其实仔细拆分下来,你主要看几个变量即可。
THREAD_POOL_EXECUTOR :这个成员变量从它THREAD_POOL_EXECUTOR = new ThreadPoolExecutor中可以看出,它是一个线程池,而ThreadPoolExecutor线程池中需要几个参数,如corePoolSize(核心线程数)、maximumPoolSize(最大线程数)、workQueue(任务队列)、threadFactory(线程工程)等等,所以像CORE_POOL_SIZE,sPoolWorkQueue ,sThreadFactory 等成员变量,只是为了配置这个线程池而已。
sDefaultExecutor 这个成员变量是默认的线程调度任务,从上面可看出SERIAL_EXECUTOR则是一个序列化的任务调度,从sDefaultExecutor = SERIAL_EXECUTOR;中可以明确的知道sDefaultExecutor任务调度中是按先后顺序执行的。
sHandler顾名思义是一个handler,mWorker是一个工作线程,mFuture则是一个FutureTask,FutureTask是专门用于管理Runnable线程的,mStatus 则是一个枚举,里面有三种状态,分别是未执行,执行中,以及执行完成状态,默认状态是未执行状态。
所以我们只要理解好上面几个变量可以不用害怕它一堆的初始化成员。
然后我们在看看AysncTask的构造方法中具体做了那些事:
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
简单来说,AsyncTask的成员变量中就只是初始化了两个变量,mWorker 和 mFuture 。这两个变量是非常重要的,后续的所有执行过程都是由这两个变量构成或引导的。
首先mWorker 是一个抽象内部类实例,是一个任务线程,它实现Callable接口中的call()方法
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
然后mFuture 则是一个针对任务线程的管理类。专门用于管理任务线程的,可以使我们的任务得到更好的控制,来看看它的构造方法吧:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
就是接受了我们的mWorker 对象以及把自身的状态设置为NEW。
以上就是在new一个AsyncTask所进行的所有操作,无非就是初始化了一些数据和变量。
下面来看看AysncTask的正式执行。
我们所知道开启一个AsyncTask任务所调用的方法是execute方法,该方法必须在主线程中调用。
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
在调用execute方法后,该方法什么也没做,只是把已初始化好的默认序列任务线程sDefaultExecutor和传递进来的数据params传递给了executeOnExecutor(),那么我们在看看这个方法做了哪些事情:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
executeOnExecutor()方法中首先判断了AsyncTask的执行状态,如果是正在执行或是已经结束执行了,它就会报出一个IllegalStateException的异常,告诉你线程或是在执行中或是已经执行完毕了。
只有在未执行的状态下,才可以进行AsyncTask请求任务,接下来它直接把AsyncTask的执行状态更改为Status.RUNNING,告诉其他任务该AsyncTask正在执行中,保持执行结果的一致性。然后就执行了onPreExecute();由于execute方法是必须在主线程中执行的,所以到目前为止还是在主线程中运行,也就证明了onPreExecute()方法是在主线程中运行的。
protected void onPreExecute() {
}
onPreExecute源码中并没有做什么事情,这对于我们来说,只需要重写该方法就可以在主线程中进行一些UI组件的初始化等操作。
- 接下来则是将我们所传递的数据赋值给mWorker的mParams变量,然后调用exec.execute(mFuture)方法,我们通过execute方法中知道exec其实就是一个sDefaultExecutor,sDefaultExecutor实则是一个SerialExecutor 序列线程,而mFuture我们在构造方法中也很清楚的知道,它是一个封装了mWorker线程的一个可管理的任务线程,那么在调用sDefaultExecutor的execute方法并传递进了mFuture任务线程,那到底做了什么事情呢,我们来看下它的源码:
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
源码我们很清晰的知道在execute方法中最终的目的就是把mFuture任务线程赋值给一个Runnable 线程并放到了THREAD_POOL_EXECUTOR线程池中,由THREAD_POOL_EXECUTOR线程池来执行mFuture线程任务。
那么接着我们看看在THREAD_POOL_EXECUTOR线程池中execute的方法中主要做了什么事情:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
THREAD_POOL_EXECUTOR线程池中主要是判断了传递的线程是否为空,是否小于当前线程池中保存的核心线程数,如果小于则直接执行addWorker(command, true)方法,下面看看addWorker方法中的实现内容:
private boolean addWorker(Runnable firstTask, boolean core) {
...(前面代码省略)
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
我们只看主要的逻辑,首先是将我们的mFuture任务线程存放到了一个Worker的对象中,然后又从Worker对象中获取到mFuture线程并赋值给了Thread ,接着把Worker对象放到workers的HashSet数据集合对象中,经过获取HashSet的大小并进行一些判断,把workerAdded 设置为true,最后开启t.start();线程,由此进入了子线程中。
那么接下来在开启的子线程中又做了什么事情呢?
我们从上面的分析指导t.start()开启就是一个mFuture的异步任务线程,那么它在哪执行呢?
细心的朋友可以发现,原来是在SerialExecutor 中的execute方法中我们的mFuture的run()早已在等待了线程的启动,那么,我现在去看看mFuture的run()方法中做了什么工作吧
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
这段代码很简单,一眼就可以看得出来就是利用我们在为mFuture初始化时传递的mWorker 对象实例并调用它的call()方法,我们先不管call怎么实现的,先来看看这个方法中的后续是什么。
接着它得到一个执行结果,并把一个boolean类型的ran设置为true,最后根据ran调用set(result);方法,并把结果传递进去,下面看看set的源码:
protected void set(V v) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = v;
U.putOrderedInt(this, STATE, NORMAL); // final state
finishCompletion();
}
}
它主要调用了finishCompletion();在来看看finishCompletion的源码:
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (U.compareAndSwapObject(this, WAITERS, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
在执行完call中,把一些对象进行还原,还调用了 done(),该方法就是在AsyncTask构造方法中我们有看到它的实现:
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
我们也说过FutureTask主要是用来管理异步线程任务的,那么在done方法中就有很好的体现,在该方法中,它会判断执行的结果是否成功,成功后有没有被发送,如果有发送它就不再发送消息,如果结果执行成功,但没有被发送它就会发送最终的执行结果:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
postResult方法的内容我们推后一点讲,那么现在我们来看看mWorker 中call()是怎么实现的:
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
在这里我们终于见到了我们所熟悉的一个方法doInBackground(),由此也可以知道其确实是在子线程运行的,而doInBackground()方法在AsyncTask类中是一个抽象方法:
protected abstract Result doInBackground(Params... params);
那么我们在重写doInBackground()时就可以直接的在其中进行一些耗时的网络和IO操作了。
这里插上一句,假如在doInBackground()调用了publishProgress方法来更新进度的话,那来看看它是怎么做的:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
publishProgress方法中主要是通过Hangler发送一条更新进度的标志用来更新进度。这里的Hangler接受消息在下面和执行结果一起讲。
最后doInBackground()执行获取的Result 结果也将会传递到postResult(result);方法中,那么现在我们来看看它的源码实现:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
postResult中首先封装了doInBackground异步执行结果的AsyncTaskResult对象,然后获取到一个Handler ,通过消息处理机制发送一条信息来切换到主线程中进行UI界面的更换,消息处理机制不属于本次博文的内容所以不再细说,那来看看这个Handler是怎么处理这个消息内容的。
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
Handler中主要是根据消息标志进行区分是更新进度还是执行结果:
如果是更新进度则调用AsyncTask的onProgressUpdate方法来更新内容,由于通过Handler已转变为主线程中,所以我们在重写该方法时可以直接更新UI组件。
如果是执行结果则AsyncTask的finish(result.mData[0]);并把结果数据传递过去,来看看finish()中是怎么实现的:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
finish()方法中也非常的简单,首先判断是否为取消线程,否则的话则执行onPostExecute(result)方法,由此我们在重写了onPostExecute方法后可以直接的更新我们的UI组件。
最后把AsyncTask的状态改为完成状态,至此整个AsyncTask生命周期就执行完毕了。
好了,至此AsyncTask整个执行过程就完全讲完了,相信大家也学到了不少东西,建议大家有空自己对着源码在梳理一遍,毕竟自己总结出来的印象就更深刻。
今天就到这里吧,祝大家生活愉快。
更多资讯请关注微信平台,有博客更新会及时通知。爱学习爱技术。
Android 源码解析之AsyncTask的更多相关文章
- Android源码解析系列
转载请标明出处:一片枫叶的专栏 知乎上看了一篇非常不错的博文:有没有必要阅读Android源码 看完之后痛定思过,平时所学往往是知其然然不知其所以然,所以为了更好的深入Android体系,决定学习an ...
- android源码解析(十七)-->Activity布局加载流程
版权声明:本文为博主原创文章,未经博主允许不得转载. 好吧,终于要开始讲讲Activity的布局加载流程了,大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与andr ...
- Android源码解析——AsyncTask
简介 AsyncTask 在Android API 3引入,是为了使UI线程能被正确和容易地使用.它允许你在后台进行一些操作,并且把结果带到UI线程中,而不用自己去操纵Thread或Handler.它 ...
- Android源码解析——LruCache
我认为在写涉及到数据结构或算法的实现类的源码解析博客时,不应该急于讲它的使用或马上展开对源码的解析,而是要先交待一下这个数据结构或算法的资料,了解它的设计,再从它的设计出发去讲如何实现,最后从实现的角 ...
- Android源码解析——Toast
简介 Toast是一种向用户快速展示少量信息的视图.当它显示时,它会浮在整个应用层的上面,并且不会获取到焦点.它的设计思想是能够向用户展示些信息,但又能尽量不显得唐突.本篇我们来研读一下Toast的源 ...
- Android源码分析之AsyncTask
AsyncTask相信从事Android开发的同学都不陌生,基本都应该用到了,和以前一样我们还是先来看看此类的summary.AsyncTask 可以确保更合理.容易的使用UI线程.这个类是设计用来执 ...
- Android 源码解析 之 setContentView
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41894125,本文出自:[张鸿洋的博客] 大家在平时的开发中,对于setCont ...
- Android源码解析——Handler、Looper与MessageQueue
本文的目的是来分析下 Android 系统中以 Handler.Looper.MessageQueue 组成的异步消息处理机制,通过源码来了解整个消息处理流程的走向以及相关三者之间的关系 需要先了解以 ...
- 【Android源码解析】View.post()到底干了啥
emmm,大伙都知道,子线程是不能进行 UI 操作的,或者很多场景下,一些操作需要延迟执行,这些都可以通过 Handler 来解决.但说实话,实在是太懒了,总感觉写 Handler 太麻烦了,一不小心 ...
随机推荐
- Android 内存管理 &Memory Leak & OOM 分析
转载博客:http://blog.csdn.net/vshuang/article/details/39647167 1.Android 进程管理&内存 Android主要应用在嵌入式设备当中 ...
- Android音视频之MediaRecorder音视频录制
前言: 公司产品有很多地方都需要上传音频视频,今天抽空总结一下音频视频的录制.学习的主角是MediaRecorder类. MediaRecorder类介绍: MediaRecorder类是Androi ...
- DB2 Enterprise Server Edition(DB2 ESE)9.1在Windows Server 2008 下出现无法新建数据库的情况,及解决办法
在安装有,DB2 9.1版本的Windows Server 2008 上面,使用默认的安装方式导致无法创建数据库,相关的错误提示: "SQL3012C 发生系统错误(原因码= "& ...
- c/c++系列的运算符优先级总结
经常写程序的时候,遇到运算符优先级的问题,令我汗颜的是,查书的次数挺多的……狠狠心,总结下.不过还要结合大量的编程实践来深入脑海. 1.首先永远忘不了的是,逗号运算符级别最低,毫无争议的还有()括起来 ...
- slf4j log4j logback关系详解和相关用法
slf4j log4j logback关系详解和相关用法 写java也有一段时间了,一直都有用slf4j log4j输出日志的习惯.但是始终都是抱着"拿来主义"的态度,复制粘贴下配 ...
- spring4+hibernate4+struts2项目整合的步骤及注意事项
首先,在整合框架之前,我们需要知道Spring框架在普通Java project和Web project中是略有不同的. 这个不同地方就在于创建IOC容器实例的方式不同,在普通java工程中,可以在m ...
- Spring配置c3p0数据源时出错报:java.lang.NoClassDefFoundError: com/mchange/v2/ser/Indirector
今天在使用Spring配置c3p0数据源时,使用的数据库是mysql,服务器是tomcat,运行时报了一个 java.lang.NoClassDefFoundError: com/mchange/v2 ...
- Cesium应用篇:3控件(1)Clock
创建 跟Clock相关的主要有Animation控件和Timeline控件,通常两者会放在一起使用. 在Cesium中,Viewer默认开启这两个控件,如果你想要不显示控件,可以在Viewer初始化中 ...
- OpenCV,计算两幅图像的单应矩阵
平面射影变换是关于其次3维矢量的一种线性变换,可以使用一个非奇异的$3 \times 3$矩阵H表示,$X' = HX$,射影变换也叫做单应(Homography).计算出两幅图像之间的单应矩阵H,那 ...
- 让你的web程序“动”起来。
看到这里你可能会问,asp.net程序本身就是动态网站,还要如何动? 我这里所谓的动起来,是指动态加载,动态更新.好吧可能你又要问了动态网站本来就是动态加载,动态更新的.asp.net的程序依附于II ...