AsyncTask, 翻译成中文就是“异步任务”,顾名思义它就是用来处理不同线程相互协作的问题的,是个轻量级的异步类。
常用的场景比如从网络下载图片并显示的过程,“下载”这个过程得放在后台线程里,而更新进度以及显示图片需要在主线程完成,这时候,用AsyncTask就很方便了。
一、AsyncTask的使用
先介绍一下AsyncTask类的一般使用方法和注意事项,然后再进行源码对照分析:
AsyncTask类是个抽象类,使用前必须继承并实现它的重要方法:
class AsyncDownload extends AsyncTask<String, Integer, Boolean> {//泛型参数Params, Progress, Result
@Override
protected void onPreExecute() {
super.onPreExecute();
XXX //操作
}
@Override
protected Boolean doInBackground(String... params) {
Boolean result;
Integer progress;
XXX //操作
publishProgress(progress);//可选。如果调用的话onProgressUpdate(progress)才会执行
XXX //操作
return result;
}
@Override
protected void onProgressUpdate(Integer... values) {
XXX //操作
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
XXX //操作
}
}
那么怎么使用呢?例如下面这样创建对象并调用execute()即可:
AsyncDownload asyncDownload = new AsyncDownload ();
asyncDownload.execute(str); //String类型参数
上面传入的str为一个String字符串。具体使用时可以按需传各种类型的参数,但是要和泛型参数的第一个参数类型保持一致。
二、AsyncTask类的方法,参数和返回值简介
上面只是个示例,这里需要介绍下这AsyncTask的几个方法和三个泛型参数。
需要注意的是,这几个方法不能手动调用,都是自动执行的,我们只需要给出具体实现即可。
而且,一个AsyncTask实例只能被执行一次,多次调用时将会出现异常 。
onPreExecute():异步任务最先被调用的方法,可以在这里做些准备操作,比如把进入条显示出来。如果不需要的话就不用实现。无参数,无返回值。
doInBackground(String… params):这个在AsyncTask类里是个抽象方法,所以必须要有实现。这个方法里用来处理后台逻辑,比如做下载操作,因为它是在新线程里运行的,在onPreExecute()之后被调用。
此方法的参数是哪里传入的呢?就是上面asyncDownload.execute(str) 传入的String字符串,这个参数类型和AsyncTask的第一个泛型参数的类型需要一致。另外此方法的返回值,需要和AsyncTask的第三个泛型参数的类型保持一致。如上面的例子,都是Boolean类型。
onProgressUpdate(progress):如果我们在 doInBackground(String… params)方法中调用了publishProgress(progress),则表示发出“进度有更新”的通知,于是onProgressUpdate(progress)会被调用。onProgressUpdate(progress)用来处理进度更新的操作,运行在UI线程,它的参数类型,和AsyncTask的第二个泛型参数的类型需要一致,上面的例子里,都是Integer类型。
onPostExecute(Boolean result): 此方法是任务结束后,被调用的方法(不管任务是完成了或者失败,都会被调用,任务取消不会被调用)。它的参数就是doInBackground(String… params)的返回值。此方法也运行在UI线程。
总结一下,除了doInBackground(String… params)运行在新线程,其他的包括调用AsyncTask的execute()方法,都运行在UI线程。
三、AsyncTask类源码解析
好了,上面抛出了AsyncTask的一些使用结论,下面,我们用源码对照分析,来证明上面的结论。
第一段:从execute() 到 onPreExecute() —————
当我们实现了一个AsyncTask,然后调用了它的execute()方法后,它是怎么工作的呢?
我们先来看android.os.AsyncTask类的execute()方法:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);//处理完成后返回自己
}
此方法调用了一个executeOnExecutor()方法来处理,传入了一个sDefaultExecutor,这个sDefaultExecutor是哪来的?
可以找到:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
所以,sDefaultExecutor就是一个静态变量。
它的实现暂时先不用管。继续看executeOnExecutor()对传入的SerialExecutor做了什么:
@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;
}
这里就是重点了,首先对此task的状态做了判断。这里先解释下:
AsyncTask会有三种状态:PENDING就绪状态,RUNNING运行状态,FINISHED结束状态。这mStatus就是表示这三种状态的标志,会随着运行而变化。这个比较简单,不细讲。
从上面可以看到,如果一个task执行了execute()时是RUNNING或者FINISHED状态,就会抛出异常。这是我们前面提到的注意点之一。
如果运行到这里,mStatus是PENDING状态,则符合运行条件,mStatus会更新为RUNNING。然后onPreExecute()就执行了。所以它是最先执行的方法。
第二段:任务的初始化 —————
接下来两行:
mWorker.mParams = params;
exec.execute(mFuture);
mWorker和mFuture是什么?追代码,这里是定义:
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
然后它们都是在AsyncTask的构造方法里被初始化的, 也就是我们new AsyncTask() 创建对象的时候,mWorker和mFuture就被初始化了:
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {//WorkerRunnable实现了Callable接口
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) {//FutureTask实现了Runnable和Future接口
@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);
}
}
};
}
看到了熟悉的doInBackground()方法,它就是在上面的call()中被调用的。那么call()又是哪里被调用呢?先不管。
明白了mWorker和mFuture是在AsyncTask构造方法中被创建的,再回头看上面蓝色字体那两行,其中exec.execute(mFuture)的exec是传进来的一个对象,也就是之前那个sDefaultExecutor,是个静态的SerialExecutor类型的对象。那么我们现在去看看这个SerialExecutor类的具体定义,以及它的execute()方法。
第三段:doInBackground() —————
SerialExecutor类是AsyncTask的一个静态内部类:
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);
}
}
}
“Serial”,翻译就是“串行化的”。那么“SerialExecutor”我们就可以猜到它是一个用于串行处理task的处理器。它是怎么实现串行的呢?
上面可以看到,类里有个mTasks,是个双端队列,当SerialExecutor的execute()执行的时候,传入的Runnable r 就会被包装成一个新Runnable对象,放入队列里。
为什么要把传入的Runnable r又包装成一个新的Runnable对象呢?这就是为了实现串行,新的Runnable对象的run()方法不仅调用了传入的r 的run() 方法,而且还调用了scheduleNext(),使得Runnable从队列中依次取出,依次执行。
类里有个mActive变量,这个变量就是正在处理的task,它是对 r , 也就是传入的mFuture(FeatureTask对象)的包装。如果它为null(第一次执行必然为null),则从队列中取出下一个Runnable对象,赋给mActive,并且用线程池来执行(THREAD_POOL_EXECUTOR.execute(mActive))。
mFuture是个FeatureTask对象,Executor执行FeatureTask时,会调用FeatureTask的run()方法,然后run()方法中又会调用构造FeatureTask对象时传入的Callable,也就是mWorker的call()方法(这部分代码就不贴了)。call()方法的实现见上面AsyncTask的构造方法中,我们再贴出来看下:
mWorker = new WorkerRunnable<Params, Result>() {//WorkerRunnable实现了Callable接口
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()是运行在新线程了。
第四段:onPostExecute() —————
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;
}
也就是把doInBackground()的返回结果,用handler进行处理了,会怎么处理呢?这里getHandler()会得到一个静态InternalHandler对象:
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;
}
}
}
上面doInBackground()返回之后,handler发送的消息是MESSAGE_POST_RESULT,也就是会走到上面result.mTask.finish(result.mData[0])这里。
这个result是什么呢?上面两块代码看看就明白,它是handler发送的消息携带的obj,也就是发送消息时new AsyncTaskResult(this, result)传入的this,也就是当前的AsyncTask对象。
它的finish()做了什么?
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
这里看到了熟悉的onPostExecute()了。很简单,如果AsyncTask对象正常执行的话,onPostExecute()就会被调用,传入的result就是postResult()传入的参数,也就是doInBackground()的返回值。同时mStatus 状态改为FINISHED。也就是这个异步任务到此结束。
第五段:onProgressUpdate() —————
在上面handler对消息的处理中,还有个case MESSAGE_POST_PROGRESS,这里也看到了熟悉的onProgressUpdate()方法。那么什么时候handler会发送这个消息呢?搜一下,找到了这一处:
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
publishProgress()无调用,需要我们自行按需调用。看到这里就很明确了,重温一下文章开始部分的那个结论:如果我们在doInBackground()里调用了publishProgress()的话,onProgressUpdate()就会得到执行。从这里就知道为什么了。publishProgress()执行时handler发送消息,然后接收消息时调用的onProgressUpdate()。
第六段:AsyncTask任务的取消 —————
还有个问题,AsyncTask的构造方法中,初始化FutureTask对象mFuture时重写了done()方法,它什么情况下会被调用呢?
mFuture = new FutureTask<Result>(mWorker) {//FutureTask实现了Runnable和Future接口
@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的cancel()方法,
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
则FutureTask的cancel()方法被调用,在那里会调用finishCompletion(),然后done()最后就会执行,这里就不贴代码了。
done()执行会怎么样?看done()里调用了postResultIfNotInvoked(get()),这个get()返回的是callable()的返回值,也就是前面mWorker的call()方法的返回值(postResult()的返回值),
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
关于postResult()可返回去看前面的分析,postResult()里handler会发送消息,如果是cancel的,那么处理消息时最后会走到onCancelled()里,onCancelled()是个protected的空方法,我们可自行实现,如果任务取消可以在这里处理。
AsyncTask的主要工作原理就分析到这里,从上面我们知道它际也是用Handler实现的异步操作,用线程池来处理任务。
第七段:AsyncTask的线程池 —————
这里对AsyncTask的线程池做个分析,从上面的源码中,可以看到,任务执行过程中用到了两个线程池:SerialExecutor和THREAD_POOL_EXECUTOR,其中SerialExecutor是用来把任务放到队列使其串行处理 ,而THREAD_POOL_EXECUTOR是真正执行任务的那个。
看下THREAD_POOL_EXECUTOR的定义:
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
其中几个参数如下:
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;//最大数量受CPU限制
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);
如果我们想让任务并行处理,怎么办呢?
AsyncTask里面有两种线程池供我们指定使用:
1.THREAD_POOL_EXECUTOR, 异步线程池
2.SERIAL_EXECUTOR,同步线程池
如果直接调用execute的话,是刚刚说的那样,先SERIAL_EXECUTOR把任务串行化,再使用THREAD_POOL_EXECUTOR执行任务。
如果想任务并行处理,可以调用executeOnExecutor(Executor exec, Params… params)方法,传入THREAD_POOL_EXECUTOR。这样就没有串行化这一步了,直接由THREAD_POOL_EXECUTOR处理。具体原理可从上面源码分析第一段和第三段找。也可以自定义线程池传入。
要注意: Android 3.0以前,默认情况下AsyncTask是串行执行,3.0以后才有executeOnExecutor方法。
四、总结
AsyncTask各个方法分工明确,使用简单方便,适用于在执行耗时任务的同时更新UI线程的情况,但是当任务特别耗时的时候,就不适合用AsyncTask了,因为AsyncTask要么串行执行,要么并行执行上对线程数量作了约束。特别耗时的任务还是采用线程池来处理比较好。