AsyncTask的一个典型的应用场景是:后台下载文件,并实时跟新下载进度。它既能使得耗时的事情在后台线程中执行,又能和主线程通信,告诉主线程更新UI。同时,AsyncTask内部使用了线程池来执行后台任务,因此它能处理多任务请求。那么它的内部是怎么实现的呢?
使用步骤
在阅读源码之前,我们还是看一下AsyncTask的使用步骤:
这部分参考了AsyncTask的基本用法 这篇博客。
1.子类化AsyncTask
2.实现AsyncTask中定义的下面一个或几个方法
2.1onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。
2.2doInBackground(Params…), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
2.3onProgressUpdate(Progress…),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
2.4onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.
3.提交任务请求
使用executeOnExecutor后者execute方法。
同时,我们还需要注意一下几点:
A)Task的实例必须在UI thread中创建
B) execute方法必须在UI thread中调用
C) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法
D) 该task只能被执行一次,否则多次调用时将会出现异常
源码分析
AsyncTask的源码不多,因此分析比较容易。我之所以要分析这个类的源码是因为我遇到了这样的困惑:既然AsyncTask使用线程池来处理多任务请求,那么我提交多个任务是不是应该这样呢?
//1 创建一个AsyncTask 的实例
AsyncTask asyncTask = new XXAsyncTask()
//2 提交多个任务
asyncTask.execute(1);
asyncTask.execute(2);
asyncTask.execute(3);
asyncTask.execute(4);
asyncTask.execute(5);
很不幸的是,程序立刻就挂掉了,问什么呢?我想的,既然AsyncTask内部有个线程池,那么我不断给它提交任务不就可以了吗?而事实证明这样不行,那么正确的姿势是怎么样的呢?
比如要提交10个任务:
for(int i=0;i<10;i++){
//1 创建一个AsyncTask 的实例
AsyncTask asyncTask = new XXAsyncTask()
//2 提交多个任务
asyncTask.execute(i);
}
也就是说要不断的new AsyncTask 的实例,这是什么情况呢?难道不是每个AsyncTask都有一个线程池吗?还真不是的。
打开源码一看,立刻明白了,AsyncTask使用的是单例模式。
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
AsyncTask构造了两个静态的线程池:THREAD_POOL_EXECUTOR和SERIAL_EXECUTOR 。因为它们是静态的,因此所有的AsyncTask对象都共享这两个线程池。也就是说,我已开始的理解就是错的,我以为一个AsyncTask内部有一个线程池,其实不然,而是所有的对象共享这两个线程池,其中SERIAL_EXECUTOR 是默认使用的线程池,当然我们可以选择使用THREAD_POOL_EXECUTOR作为线程池的,主要用到executeOnExecutor方法,比如:
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,1);
理解了这点后,我们看看AsyncTask的具体内部实现。
当我们创建好AsyncTask实例后,采用默认的线程池的情况下,我们会执行excute方法,这个方法如下:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
调用executeOnExecutor方法进一步处理,注意传给executeOnExecutor方法的参数sDefaultExecutor定义如下:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
也就是说默认的线程池是SERIAL_EXECUTOR了。
然后我们看一下executeOnExecutor是如何进一步处理的:
@MainThread
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;
}
该方法中出现的Status定义如下:
public enum Status {
/**
* Indicates that the task has not been executed yet.
*/
PENDING,
/**
* Indicates that the task is running.
*/
RUNNING,
/**
* Indicates that {@link AsyncTask#onPostExecute} has finished.
*/
FINISHED,
}
Status中只定义了三种状态,因此,asyncTask的状态如果不是Status.PENDING,就会抛出异常。这是为什么我们向下面这样使用程序会挂掉:
//1 创建一个AsyncTask 的实例
AsyncTask asyncTask = new XXAsyncTask()
//2 提交多个任务
asyncTask.execute(1);
asyncTask.execute(2);
asyncTask.execute(3);
第一次调用execute的时候,asyncTask的状态由Status.PENDING转为Status.RUNNING,第二次调用的是有,asyncTask的状态还是Status.RUNNING,因此程序就会挂掉了。
step 1
接下来会调用onPreExecute()方法,也就是说这个方法是在UI线程中调用的,我们完全可以在其中更新UI。做一个初始化工作,这个时候,我们提交的任务还没有执行。继续往下看。
step 2
接下来调用了:
exec.execute(mFuture);
exec就是excute方法中传入的sDefaultExecutor ,它默认初始化为SERIAL_EXECUTOR,SERIAL_EXECUTOR的定义如下:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
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);
}
}
}
exec.excute方法也就是这里的execute方法了。这里构造了一个Runnable的实例并把它添加到mTasks 中,第一次执行到这里mActive 肯定为null,因此会调用scheduleNext方法,这个方法使用 mTasks.poll()去除之前添加的Runable,然后执行THREAD_POOL_EXECUTOR.execute(mActive),从而把Runable添加到线程池中。
step 3
Runnable中调用了传入的Runnable的run方法,这个Runable就是mFuture,mFuture定义如下:
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);
}
}
};
mFuture 是一个FutureTask的实例,并且这个实例接受一个mWork参数,这个mWork是一个和mFuture一样顶一个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);
}
};
mWorker 其实是一个Callable的实例了,他有个call方法。而mFuture本质上是一个Runnable的实例,调用mFuture的run方法其实就是调用FutureTask中的run方法,FutureTask接受了mWorker 作为参数,并把它赋值给自己的callable 属性,代码如下:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
step 4
所以我们看看FutureTask的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 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);
}
}
这个方法做了如下事情:
1.调用callable的call方法,也就是mWork的call方法,mWork的call方法step 3中已经贴过,其中会调用doInBackground方法,也就是我们提交的需要在后台执行的代码了。因此,doInBackground是在mWork的call方法中被执行的,mWork的call方法又是通过THREAD_POOL_EXECUTOR.execute(mActive)被添加到线程池后被执行的。
2.调用postResult方法
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
postResult方法会发送一个消息,这个消息会使得InternalHandler的handleMessage方法被调用。这个时候,我们已经离开了后台线程,又回到UI线程了。因为InternalHandler的Looper是UI线程的Looper,其构造函数中有如下代码可以知晓:
public InternalHandler() {
super(Looper.getMainLooper());
}
注意我们获取的消息为MESSAGE_POST_RESULT,因此根据handleMessage的定义:
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;
}
}
会调用到AsyncTask的finish方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
step 5
这个方法中,我们没有调用取消方法的话,就会调用onPostExecute方法了,这样,我们使用AsyncTask需要覆写的几个方法只有onProgressUpdate方法没有调用了。那么这个方法是怎么被调用的呢?我们说,我们要更新进度的话,需要使用publishProgress方法,我们看看这个方法:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
这个方法就是发送一个消息,然后还是InternalHandler方法handleMessage方法被调用,注意这会消息是MESSAGE_POST_PROGRESS,因此根据源码,再贴一次handleMessage方法吧:
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;
}
}
可见这次调用的是onProgressUpdate方法。至此,所有需要覆写的方法的调用过程我们都分析结束了。