在之前的博客,我们分析了Handler的源码,在我看来Handler的源码只要理清了流程还是比较简单的。而这次要讲解的AsyncTask的源码我自认为会比Handler稍微复杂一点,我们今天一起来了解一下。
在了解AsyncTask前,你需要先了解以下内容:
1.线程池
2.AsyncTask的基本使用的结构
3.Handler的基本使用
好,我们开始进入正题,还是拿我们之前举的那个例子
AsyncTask task = new AsyncTask<Void, Integer, Void>() {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (isCancelled()) {
return;
}
}
@Override
protected Void doInBackground(Void... voids) {
publishProgress();
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
@Override
protected void onCancelled() {
super.onCancelled();
}
};
task.execute();
这个是完整AsyncTask的使用的方式,但是实际上在我们创建AsyncTask中只需要重写一个doInBackground方法即可。其他的方法都可以不用重写。那么为什么这样呢?因为AsyncTask是一个抽象类,而他的抽象方法有且只有doInBackground这一个。所以更具抽象类实例化必须要实现他的抽象方法这个规则,所以我们只需要重写一个doInBackground方法。
好了简单的介绍了AsyncTask的使用之后,我们开始进入源码层面剖析AsyncTask。
回想一下,我们使用的AsyncTask的过程只需要创建一个AsyncTask的对象,重写他的子方法,然后启动这个AsyncTask就可以了,那么我们就根据这个流程来分析他的源码。
一、创建AsyncTask
一般我们在创建AsyncTask中使用的是下面的这个方法
public AsyncTask() {
this((Looper) null);
}
而这个方法会调用另外一个构造器
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return 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);
}
}
};
}
首先,这里先根据传入的 Looper 来创建一个Handler,这个mHandler在AsyncTask中至关重要,AsyncTask要把doinBackground中的执行结果传递给onPostExecute,即从后台运行耗时操作切换到ui线程,都是靠这个mHandler进行的线程的调度。
然后构造器里面又创建了两个对象,mWorker,mFuture。这两个对象也是非常重要的,下面我先简单的介绍一下。
mWorker的类型是WorkerRunnable,这个类很具有迷惑性,但是千万不要把它认作是执行一个后台操作的类,子线程只是调用了他的call方法。这个类的申明是在AsyncTask.java中的,是一个内部静态的抽象类。这个抽象类的构成很简单,我们可以从他的泛型中看出来,他的泛型一个是 mParams,传入之后作为一个变量保存。另一个Result即为call方法的回调,确定参数类型。
再看mFuture,它的类型是FutureTask,进入到这个类的源码,发现这个类实现了RunnableFuture接口。而这个借口又实现了Runnable接口,这个时候我们就知道了,真正执行线程方法的是这个类。这个类把上面的mWorker作为参数传进去了,又规定了一个泛型Result,定义了一个方法done()。
为了更好的理解AsyncTask的完整流程,在这一步中,我们暂时不进行call()与done()方法内容的讲解。至此第一步中的主要操作都在这里完成了。
第二步 实现AsyncTask的子方法
这一步很简单,也没有什么要详细说的。这里主要提一下各个方法,为第三步做铺垫。
AsyncTask可重写的方法 onPreExecute onProgressUpdate doInBackground onPostExecute onCancelled。
另外可在AsyncTask还有两个很重要的方法也在这里提一下,一个是 publishProgress(),一个是 cancel()。
具体这些方法是怎么工作的我们将会在第三步重点查看。
第三步 调用方法启动AsyncTask
在日常开发的过程中,我们常用 execute或者executeOnExecutor 来执行AsyncTask,这两个的区别是前者是使用一个叫sDefaultExecutor线程池,而后者用的是我们自定义的线程池
为了方便理解,我们先暂时不对sDefaultExecutor进行讲解,这个线程池我们将会在最后进行讲解
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
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的状态做了判断,根据抛出异常的msg,我们可以得出结论一个AsyncTask只能被启动一次。
接下来AsyncTask把自己的状态设置为正在运行,然后调用了onPreExecute()方法,此时我们的线程还未被切换,所以这里可以做一些线程未被切换的操作(因为一般传入的线程都是UI线程,所以这里的onPreExecute也是在UI线程中,onPreExecute常用于显示提示框之类)
然后程序把传入的参数复制给了mWorker这个对象中,然后调用了传入线程池的execute方法来执行线程(mFuture)。
接下来我们就得到Future这个类里面了,由于他被线程池所执行了,所以这里调用的是他的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);
}
}
这里的代码也很清晰,首先先进行一次Future状态的判定。然后他会去取他的成员变量callable,这个callable我们可以从Future的构造器中得知就是之前我们在第一步中传入的mWork,他的泛型V就是我们所输出的结果的类型。接下来他就调用了callable的call方法,这时候我们再看一下第一步中我们暂时没有看的代码
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
这边我们重点要看的是 result = doInBackground(mParams) 这行代码,这里的mParams就是第三步一开始我们讲的那个 mWorker.mParams = params 赋值而来,而这个doInBackground对应的就是我们必须要重写doInBackground。这里一开始就讲了是启动了一个子线程,对应的doInBackground操作就当然在子线程中完成的了。
我们继续看下去,这边不管是否出错他都调用了postResult这个方法,我们进入到这个方法中看一下
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
可以看出来由于目前在子线程,这边使用了Handler进行线程间消息的通知,这个Handler就是我们第一步中创建的那个。而这个message的obj是创建出来的一个新的对象,这个对象把得到的结果存到了自己变量中。
这里要注意一下,这里涉及到了线程的切换,所以接下来我会分主线程以及线程池中的线程进行讲解
在主线程中
在主线程中,我们需要看一下handler中对应的方法。
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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;
}
}
}
这边采用了一个私有的内部静态类来申明了这个Handler。我们看一下对应的那个方法,很简单就是result.mTask.finish(result.mData[0]) 这么一行代码,这行代码要对应 Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this, result)) 一起看。
对应看我们就可以知道这个result.mTask.就是自己,所以我们继续找AsyncTask的finish这个方法。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
这个方法很简单,先判断isCancelled,如果true,调用onCancelled方法,如果false调用onPostExecute方法,最后把AsyncTask的状态设置成finished。
这里我们关注一下isCancelled方法,他所关联的变量是 mCancelled ,我们看一下这个变量还有在哪里用到的。
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
public final boolean isCancelled() {
return mCancelled.get();
}
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
从这个几个赋值的地方可以看出来被调用cancel方法和程序异常的时候,isCancelled的值为true。
其实到这里为止我们AsyncTask的流程大体上已经完成了,但是为了严谨我们还是把另外一部分给讲完。
子线程
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 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);
}
}
回到这里,执行完call方法的之后会调用set(result)方法,然后会执行以下流程
protected void set(V v) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = v;
U.putOrderedInt(this, STATE, NORMAL); // final state
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
}
这两个方法最后调用done(),这个done方法对应的就是我们在初始化的时候创建的mFuture里面的done,代码如下
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);
}
}
};
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
乍一看代码,这里又postResult一遍,但是由于之前call方法中第一行就把这个值设成true,所以这个方法最后是不执行的。
讲到这里基本上已经把大部分的AsyncTask的流程都讲完了,接下来要讲的是 publishProgress 这个方法,让我们看一下源码
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
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;
}
}
还是想handler发送了一条消息,然后handler取出他的mDate,也就是之前传入的values,调用AsyncTask的onProgressUpdate方法,这样publishProgress也就讲完了,那么整个AsyncTask的流程就讲完了。
写到这里感觉这篇文章有点过长了,太长的文章不仅作者写起来费劲,读者读起来也费尽。那么AsyncTask的总结与自身的默认线程池将放在下一篇博客中讲解=。=