今天学习了AsyncTask的源码。打算把目前学到的写下来记录一下。可能有很多地方我还掌握得不太好,不过一步步来嘛。先理解着记啦。
先稍微复习一下AsyncTask里面重要的一些方法吧~
1. onPreExecute()
该方法将在执行实际的后台操作前被UI 线程调用。可以在该方法中做一些准备工作,比如显示“正在加载”的进度条。
2. doInBackground(Params...)
将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。
比如从网络拉取预览图, 每拉取一张以后, 可以调用 publishProgress方法来更新一张默认的图片。
3. onProgressUpdate(Progress...)
在publishProgress方法被调用后,这个方法将被UI线程调用, 用于更新进度等界面显示。比如调用这个方法更新一张图片。
4. onPostExecute(Result)
在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.
今天的顺序,是打算由表及里,从AsyncTask调用的最后一个方法开始,追本溯源,去探寻这个类具体的实现方法。
@MainThread
public final AsyncTask 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;
}
// TIP: mWorker是一个自定义的类,由于实现了Callable接口,因此可以被当做Callable类型的变量使用
private static abstract class WorkerRunnable implements Callable {
Params[] mParams;
}
/* Callable接口类似于Runnable,但是Runnable不会返回结果,并且无法抛出返回结果的异常。
而Callable功能更强大一些,被线程执行后,可以通过Future对象拿到异步计算的结果(即可以拿到返回值)。*/
可以看到,这个方法是在主线程中调用的。它在第一行做了判断,只有当目前任务的状态为等待,也就是初次创建尚未执行时,才能正确执行。否则将会抛出异常。(这里也能看出,一个AsyncTask对象只能被执行一次。否则会报错)。
接下来,先修改任务状态为运行,再调用onPreExecute()方法中做一些UI的准备工作(如显示几张默认的预览图,或者显示进度条等),之后把mWorker参数的mParams变量设为声明AsyncTask类时传入的Params变量(即为第一个参数)。如果参数是null,可以忽略这一局。
最后这一步很重要,调用了exec的execute()方法。
从该方法传入的参数中可以看出,exec是第一个参数,是一个Executor类型的变量。Executor是一个工具,使用线程池来管理线程,可以重复利用已经创建出来的线程而不是每次都必须新创建线程,节省了一部分的开销。那么,这一句代表程序在子线程启动了mFuture任务。
那么,到底什么是mFuture呢?我们可以跟踪它,在这个类前面的声明和方法中找到答案。
private final WorkerRunnable mWorker;
private final FutureTask mFuture
public AsyncTask() {
mWorker = new WorkerRunnable() {
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(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);
}
}
};
}
可以看出,mFuture是一个FutureTask类型的成员变量,并且在构造方法里得到初始化。因为FutureTask类实现了RunnableFuture接口,所以能够被传入参数类型要求为Runnable的execute方法。
这时,我们需要留意一个事情:mWorker变量是用来做什么的?我们发现,WorkRunnable的call()被重写,在mWorker的call()方法中,启动了doInBackground()方法,可是我们知道,这个方法是用来在子线程中处理耗时操作的。它是怎么跑到子线程里面去的呢?
接着往下看,在mFuture对象的构造函数中,传入了mWorker参数,又因为mFuture是在子线程中被启动的,这就说明,在FutureTask内部用到了mWorker,所以mWorker的call()方法里里可以调用只能在子线程调用的doInBackground()方法。
我们深入到FutureTask类里面去看一下,里面是怎么用到mWorker的,以此来印证上文的猜测。
// 在FutureTask类中
private Callable callable;
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable 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 must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
在构造方法中,mWorker传入FutureTask类之后,被赋给了该类的callable成员变量。同时把标志状态的变量state初始化为NEW。
// 在使用过程中,state可能的变化形式
Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
在run()方法里,我们一步步来分析。如果state此时不为NEW,即该任务已经被启动过,那么直接return,不再进行下面的操作。这也直接反映了,为什么一个FutureTask对象只能执行一次,多次调用run()方法无效。
/** Once the computation has completed, the computation cannot be restarted
* or cancelled (unless the computation is invoked using
* {@link #runAndReset}). */
然后,把callable赋给一个新的变量c,并调用c的call()方法执行任务,这个call()方法就是用来执行我们要完成的耗时任务。如果执行时没有抛出异常,那么任务执行完毕后,调用set()方法设置输出结果。输出结果的具体过程,我们继续来跟踪:
/** The result to return or exception to throw from get() */
private Object outcome;
protected void set(V v) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = v;
U.putOrderedInt(this, STATE, NORMAL); // final state
finishCompletion();
}
}
在set()方法中,设置了输出变量outcome,之后调用了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
}
这个方法前面的for循环部分无关紧要,不用研究,直接从倒数第二句开始看,先调用done()方法,再把callable变量置为null,减少资源占用。
这时候你会发现,怎么看了这么多方法,还没有调用到尽头,到底什么时候结束啊。暂时先不要急,跟踪进入done()方法,我们就会惊喜地发现:
protected void done() { }
done()方法,里面什么也没有,是需要我们在使用FutureTask类的时候自己来实现的方法!这一连串的调用总算在这里告一段落!高兴过后,再想一想:done()方法出现在这里,意味着什么呢?
我们回头捋一遍,从进入FutureTask类开始,先找到它的run()方法,查看它是怎么执行的,如果这个任务刚刚被创建,还未执行,那么便启动它,并把结果用set()方法进行设置,最后来到可以自定内容的done()方法。显然,done()方法是在传入的mWork任务执行完毕后调用的方法,我们可以在里面写一些任务一旦执行完毕后需要做的事情,举例的话这里就先不举了。
现在又有了一个疑问,既然有了set()方法,那一定也有一个与之对应的get()方法。ctrl+F 在FutureTask类里找到这个方法:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
在report()方法里,返回了之前在set()方法中设置过的outcome变量,即返回了任务执行的结果。接下来,ctrl+F搜索整个FutureTask类,却发现,并没有找到get()方法被调用的地方。那么它到底是在哪里用到的呢?
要解决这个问题,就需要返回刚刚的AsyncTask的构造方法中一探究竟了。至此,也标志着FutureTask类的探究可以基本结束。
public AsyncTask() {
mWorker = new WorkerRunnable() {
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(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);
}
}
};
}
public final Result get() throws InterruptedException, ExecutionException {
return mFuture.get();
}
显而易见,在done()方法的第二行,就调用了mFuture的get()方法。
真是得来全不费工夫。经过之前的探索,你应该也知道下一步是做什么了,没错,进入postResultNotInvoked()方法,来看看这里面做了些什么~
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
/* TIP: AtomicBoolea是JAVA中的一个原子变量,
在这个Boolean值的变化的时候不允许被其他线程打断,保持操作的原子性。*/
有没有发现一个很奇怪的问题?如果wasTaskInvoked的值是false,才会执行postResult()方法,否则将什么也不做。可是在call()方法中的第一句:
mTaskInvoked.set(true);
就已经设置它为true,并且在AsyncTask类中的其他地方并没有再更改这个设置。也就说明它将一直为ture,那么,postResult()方法将一直不会被执行。
由此我们可以认为,在done()方法里调用的postResultIfNotInvoked(get())只是一个对任务执行出错与否的检验,真正的任务执行结束之后返回结果的语句并不在这里。
那么这个结果,究竟是在哪里被返回了呢?
继续研究call()方法,里面的最后一句代码揭开了这个谜底:
return postResult(result);
终于来了——postResult()。
不过先不急着跟进去看我们先回忆一下整个call()方法的调用经过
在FutureTask类的run()方法中,先把mWorker赋给里面的变量c,再通过调用c.call()执行耗时操作,执行结果赋给变量result。之后,用set(result)方法设置了输出值outcome。
回到AsyncTask类里,mFuture的get()方法能够获得这个输出值,可是在done()方法中,如果程序正常运行,我们并没有机会用到它。
所以,唯一能正常拿到这个执行结果result的地方,就是call()执行结束后返回的postResult(result)方法。
好啦,到这里逻辑依旧很清晰我们可以继续跟进去看一看了
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
在这个方法里,除了返回结果,还有一些操作:message被用handler组装了一下,其中包括一个奇怪的常量MESSAGE_POST_RESULT,和源码自定义的AsyncTaskResult类的匿名对象。最后被发送了出去。
要想搞清楚这个消息的意义,还需要看一下getHandler()方法的具体内容:
private static InternalHandler sHandler;
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
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:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
经过一系列的追踪,来到了InternalHandler类的handleMessage方法,看到了之前那个奇怪的MESSAGE_POST_RESULT的使用含义。
话不多说,来看switch语句的第一个case吧~不出意料,这里又来了一个调用——result.mTask.finish(result.mData[0])。(为了不耽误程序运行流程,第二个case在后面再进行介绍)
从result开始看起吧~这个对象所属的类是在AsyncTask类里自定义的一个内部类,具体实现如下:
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
可以看到mTask是该类里的一个AsyncTask类型的成员变量,那么,result.mTask.finish(result.mData)不正是意味着调用了AsyncTask的方法嘛?
接下来快速找到finish()方法,一鼓作气,我们速战速决,看看它都搞了些什么事情:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
/* TIP:isCancelled()的意义是,当任务在正常结束之前取消,该方法返回true,否则返回false。
这就表明,如果任务正常结束了,isCancelled()返回值为false,程序跳到else语句的内容* /
在finish()方法中,如果任务正常结束,便调用onPostExecute()方法,回到UI线程(主线程),将后台(子线程)的计算结果在UI中显示。
不过这时候就有个问题了,目前我们所做的一系列操作都是在子线程中进行的,那么是怎么切换到主线程来更新UI的呢?
答案就在之前带领我们一步步查看到这里的postResult()方法中:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
简单来说,是这里面用到的message.sendToTarget()方法,把刚刚用handler组装好的消息添加到了后台的消息队列里,主线程会不停地读取消息队列来作出更新操作,当读取到这条消息之后,就可以更新UI了。这样就实现了子线程和主线程之间的通信。
最后,修改状态标志位mStatus为FINISHED。
还记得在文章开始,最先看到的executeOnExecutor()方法吗?
@MainThread
public final AsyncTask 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;
}
当状态标志位修改为FINISHED后,该任务便不能被重新执行了,否则会报错。我们兴奋地发现,到这一步,已经与原点衔接上了!
不过还有一点没有介绍,就是之前提到过的handleMessage()方法的第二个case:MESSAGE_POST_PROGRESS。
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult
通过在doInBackground()方法中调用publishProgress(),会向消息队列发送这一类型的消息,主线程通过读取消息队列获取消息,来更新进度条的进度数字。至于这其中的逻辑——还记得之前handleMessage()方法中的那第二个case嘛?
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
@MainThread
protected void onProgressUpdate(Progress... values) {
}
在publishProgress()发送消息后,会在第二个case中调用onProgressUpdate()方法,它的具体内容需要我们自己来写,这个方法可以实现在主线程更新进度条UI,大家也都用得很多。
至此,AsyncTask的4个重要的方法:
- onPreExecute()
- doInBackground(Params...)
- onProgressUpdate(Progress...)
- onPostExecute(Result)
也在分析中一个一个地见到并解释完了。
文章到这里就要结束了,不知道大家对是不是对AsyncTask内部的机制有一个更清晰的认识和了解了呢~
最后,作者尚在学习,欢迎指摘~