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的更多相关文章

  1. Android源码解析系列

    转载请标明出处:一片枫叶的专栏 知乎上看了一篇非常不错的博文:有没有必要阅读Android源码 看完之后痛定思过,平时所学往往是知其然然不知其所以然,所以为了更好的深入Android体系,决定学习an ...

  2. android源码解析(十七)-->Activity布局加载流程

    版权声明:本文为博主原创文章,未经博主允许不得转载. 好吧,终于要开始讲讲Activity的布局加载流程了,大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与andr ...

  3. Android源码解析——AsyncTask

    简介 AsyncTask 在Android API 3引入,是为了使UI线程能被正确和容易地使用.它允许你在后台进行一些操作,并且把结果带到UI线程中,而不用自己去操纵Thread或Handler.它 ...

  4. Android源码解析——LruCache

    我认为在写涉及到数据结构或算法的实现类的源码解析博客时,不应该急于讲它的使用或马上展开对源码的解析,而是要先交待一下这个数据结构或算法的资料,了解它的设计,再从它的设计出发去讲如何实现,最后从实现的角 ...

  5. Android源码解析——Toast

    简介 Toast是一种向用户快速展示少量信息的视图.当它显示时,它会浮在整个应用层的上面,并且不会获取到焦点.它的设计思想是能够向用户展示些信息,但又能尽量不显得唐突.本篇我们来研读一下Toast的源 ...

  6. Android源码分析之AsyncTask

    AsyncTask相信从事Android开发的同学都不陌生,基本都应该用到了,和以前一样我们还是先来看看此类的summary.AsyncTask 可以确保更合理.容易的使用UI线程.这个类是设计用来执 ...

  7. Android 源码解析 之 setContentView

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41894125,本文出自:[张鸿洋的博客] 大家在平时的开发中,对于setCont ...

  8. Android源码解析——Handler、Looper与MessageQueue

    本文的目的是来分析下 Android 系统中以 Handler.Looper.MessageQueue 组成的异步消息处理机制,通过源码来了解整个消息处理流程的走向以及相关三者之间的关系 需要先了解以 ...

  9. 【Android源码解析】View.post()到底干了啥

    emmm,大伙都知道,子线程是不能进行 UI 操作的,或者很多场景下,一些操作需要延迟执行,这些都可以通过 Handler 来解决.但说实话,实在是太懒了,总感觉写 Handler 太麻烦了,一不小心 ...

随机推荐

  1. 借助node实战JSONP跨域

    一.前言: 浏览器安全是基于同源策略的.所谓同源策略就是三相同: 1.协议相同: 2.域名相同: 3.端口相同. 但,凡事都是有利弊,同源策略也导致了我们想用AJAX跨域请求,但NO!!为了规避这种限 ...

  2. 辨析relative与absolute

    谈起它们,想必大家都不陌生.relative,相对定位嘛:absolute,绝对定位嘛.但是它们到底是个啥东东呢? 看看w3c的定义,见下表 定位 含义 relative 元素框偏移某个距离.元素仍保 ...

  3. karma单元测试入门

    学习angularjs,都会遇到karma单元测试,可是初学者面对复杂的测试配置往往不知从何入手,下面我们将抛开angularjs,单独使用两个js文件,完成一次测试入门. 0,karma原理

  4. C# Excel 为图表添加趋势线、误差线

    Excel图表能够将数据可视化,在图表中另行添加趋势线和误差线,可对数据进行进一步的数据分析和统计的可视化处理.Excel中的趋势线可用于趋势预测/回归分析,共6中类型:指数(X),线性(L),对数( ...

  5. iOS编译FFmpeg、kxmovie实现视频播放 (转载)

    由于FFmpeg开源框架的功能非常强大,可以播放的视频种类很多,同时添加第三方库kxmovie,实现视频播放,真的是爽爆了,因此今天来说一下关于FFmpeg在iOS手机上的一些配置过程,配置工具,还有 ...

  6. canvas实现拖动页面时显示窗口视频

    简介 当前主流的视频网站目前有不少新鲜好玩的功能,最明显的莫过于小视频的显示--当视频不在当前视口范围 时,会在右下角用一个小窗口来显示当前的视频,而且可以拖拽. 今晚心血来潮,起了动手试试的念头.我 ...

  7. 总结Oracle删除表空间遇到的问题

    问题1:删除表空间期间遭遇报错 ORA-29857 问题2:删除表空间期间遭遇 ORA-02429 问题3:表空间删除完毕,主机磁盘空间不释放 问题1:删除表空间期间遭遇报错 ORA-29857 删除 ...

  8. selenium操作隐藏的元素

    有时候我们会碰到一些元素不可见,这个时候selenium就无法对这些元素进行操作了.例如,下面的情况: Python 页面主要通过“display:none”来控制整个下拉框不可见.这个时候如果直接操 ...

  9. ssh整合问题总结--运行项目时报java.lang.StackOverflowError(堆栈溢出)异常

    今天在整合ssh项目中,碰到一个异常,当我提交购物车数据到订单时,浏览器报了一个这样的异常. 当时,我就吓坏了.尼玛,这不是内存溢出了吗?吓得我赶紧去检查了每一个有遍历语句的代码,结果没有发现一个死循 ...

  10. 一种C#生成符合Java规则的二进制文件方法

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.一个项目中的真实问题 实际项目中,本想通过C#制作小工具生成SHP ...