一,写在前面
1.1,AsyncTask的使用场景:开线程执行耗时的任务,并需要在主线程操作UI。值得一提的是,AsyncTask并不适用于做特别耗时的操作,特别耗时的操作可以用线程池+Handler来处理。
1.2,本篇文章着重讲述AsyncTask的原理,不再介绍具体如何使用AsyncTask。在使用AsyncTask时会介绍它的几个方法,这里先直接给出一些结论,后面会具体分析。方法如下:
void
onPreExecute():主线程中被调用,执行异步任务前被调用,用于做一些准备工作。
Result
doInBackground(Params... params):子线程中被调用,用于做一些耗时的操作。参数params,调用execute方法时传入;返回值Result,作为参数传入onPostExecute方法。
void
onPostExecute(Result result):主线程中被调用,用于操作UI。执行完异步任务后被调用,也就是doInBackground后被调用,参数result是方法doInBackground的返回值。
onProgressUpdate(Progress... values):主线程中被调用,用于操作UI,显示异步任务的执行进度。在异步任务执行过程中,也就是在doInBackground方法中调用publishProgress方法,该方法会被调用。
void
onCancelled():异步任务被取消是调用,也就是cancel方法取消异步任务时被调用。
1.3,使用AsyncTask注意要点(后面具体分析):
1,AsyncTask类需要在主线程中完成加载,否则执行完异步任务后无法操作UI;
2,AsyncTask对象在主线程中创建,且execute方法也在主线程中执行。
3,上面提到那些方法均是被回调,不要在程序中直接调用。
4,Android1.6以前,AsyncTask是串行执行任务;在Android1.6~3.0版本里,AsyncTask采用了线程池并发执行任务;Android3.0开始,AsyncTask在原有线程池基础上,又添加了一个SerialExecutor线程池,用于串行执行任务,避免之前版本并发执行产生的同步问题。本篇文章是在Android3.0以后的版本进行分析,也就是AsyncTask串行执行任务。
5,AsyncTask对象只能调用一次execute方法,否则会抛出异常,也就是说一个AsyncTask对象只能执行一个任务。
另外,在阅读本篇文章前,建议先了解Handler机制,以及线程池的知识,本篇本章将不再阐述相关要点。可以参考文章:
二,AsyncTask源码分析
在使用AsyncTask执行一个异步任务的时候,若是需要串行执行任务,可以调用AsyncTask的execute方法。在查看该方法前,先来看看AsyncTask构造方法做了一些什么。 查看源码如下:
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
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 occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
第2行,创建了一个WorkerRunnable的匿名子类对象mWorker。WorkerRunnable是AsyncTask的内部类,结构:private static abstract class
WorkerRunnable<Params, Result> implements Callable<Result>,它是一个抽象类,实现了Callable接口。
查看Callable源码:
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Callable接口只有一个call方法,由于不是本篇文章重点,这里就不深究Callable了,有兴趣的哥们可以在网上搜索Callable+Future的使用。
回到构造方法的第12行,创建FutureTask的匿名子类对象mFuture,且是有参的构造方法,参数mWorker就是WorkerRunnable的匿名子类对象。
查看其结构:public class FutureTask<V> implements RunnableFuture<V> , public interface RunnableFuture<V> extends Runnable, Future<V>。也就是说,FutureTask实现了Runnable接口和Future接口,由于实现了Runnable接口,可以把FutureTask看做是一个任务。
接下来查看AsyncTask$execute方法源码:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
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;
}
第6行,调用了executeOnExecutor方法,参数sDefaultExecutor是一个SerialExecutor对象,见第1,2行。值得一提是,SerialExecutor是AsyncTask的内部类。
第13行,在执行任务前,会对变量mStatus进行检查。mStatus有三种值:PENDING表示任务还没开始执行,RUNNING表示任务正在执行中,FINISHED表示任务已经执行完成。因此execute只能被调用一次,否则会抛出异常。
第25行,设置变量mStatus的值为RUNNING。
第27行,调用onPreExecute(),在任务执行前做一些准备工作,此时逻辑仍在主线程。
第30行,调用SerialExecutor$execute方法,且参数是FutureTask的匿名子类对象mFuture。
查看AsyncTask$SerialExecutor$execute方法源码:
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);
}
}
}
第2行,ArrayDeque是一个泛型的队列类,这里泛型声明的是Runnable,ArrayDeque可以对Runnable对象进行入队和出队的操作。例如,入队顺序是R1,R2,R3,那么出队顺序是R1,R2,R3。ArrayDeque并不是一个多么神秘的类,查看其结构的源码发现它本质是一个Collection集合,同样具有存储,删除,查询数据的操作。ArrayDeque提供了offer方法将数据对象放到队列的队尾,提供了poll方法将队首的数据对象移除出队列,并返回该数据对象。
第3行,定义一个Runnable对象,用于存放队列中取出的Runnable对象。
第6行,调用ArrayDeque$offer方法,将一个Runnable任务添加到队列里,这里称这个Runnable任务为
M。需要注意的是,
任务M并不是mFuture,它是只是一个普通的Runnable对象,里面封装了变量mFuture。看代码时,先不要管任务M里的这个run方法,只需要知道添加了一个任务M到队列里。
第15行,第一次调用AsyncTask$execute方法时,mActive为null,会调用scheduleNext()。
第21行,取出队列中处于队首的任务M,并将返回值也就是任务M,赋值给mActive。这样当在主线程中
再次执行AsyncTask$execute方法时,新的任务M会加入队列,但由于mActive不为空,就不会调用scheduleNext()。那么新的任务M何时才能出队列呢,接着往下看。
第22行,使用THREAD_POOL_EXECUTOR线程池执行任务M,逻辑会执行到第7行的run方法。在该run方法里,执行了r.run(),也就是执行了mFuture的run方法。mFuture的run方法才是真正执行了异步任务(后面会具体分析),它是一个耗时操作,在执行完异步任务后才会执行finally里的代码,并不断重复该操作。
THREAD_POOL_EXECUTOR是一个什么样的线程池呢?查看其源码:
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);
/**
* 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);
ThreadPoolExecutor是Java提供的API,它是接口Executor唯一的实现类,关于它的介绍见文章
关于Android中常用的四种线程池的介绍 ,这里不再重复阐述。线程池THREAD_POOL_EXECUTOR有这样一些信息:核心线程数是cpu数加1,最大线程数是2倍的cpu数加1,非核心线程闲置超过1s会被系统回收,任务队列容量为128个。
值得一提的是,使用线程池执行一个新的任务时:
1,若线程池中仍有核心线程未使用,则启动核心线程来执行任务;
2,若核心线程均在执行任务,将任务放入工作队列中(工作队列未满);
3,若工作队列已满,启动一个非核心线程来执行任务(线程池中的线程未超过最大线程数);
4,若线程池中的线程超过最大线程数,任务被拒绝执行,默认是抛出异常给调用者。
小结:在Android3.0以后,调用AsyncTask$execute方法执行异步任务,是以串行的方式执行任务。
AsyncTask使用了SerialExecutor线程池,先将任务放在队列中,第一次执行任务会调用scheduleNext方法取出队列里任务,并采用THREAD_POOL_EXECUTOR线程池执行任务。在任务执行完毕后,才调用scheduleNext方法取出下一个任务并执行之,否则不会取出队列里的任务。
三,采用线程池THREAD_POOL_EXECUTOR,执行异步任务的过程
前面讲到真正执行异步任务是由线程池THREAD_POOL_EXECUTOR完成,最终会调用变量mFuture的run方法。
mFuture是一个FutureTask类型的变量,查看FutureTask$run方法源码:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public void run() {
//...
Callable<V> c = callable;
if (c != null && state == NEW) {
//...
result = c.call();
//...
}
//...
}
第13行,变量callable在构造函数中初始化,即前面提到的WorkerRunnable类型的对象mWorker。
第18行,调用Callable$call方法,即WorkerRunnable的call方法。
查看变量mWorker初始化的源码:
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
第7行,先执行doInBackground(mParams),并将其的返回值作为postResult方法的参数。需要注意的是,此时任务的执行仍在线程池THREAD_POOL_EXECUTOR中,因此doInBackground方法是在子线程中被调用,可以重写该方法并执行耗时操作。
查看AsyncTask$postResult方法源码:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
//继续查看...
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
第3行,创建一个Message,消息的唯一标示what是MESSAGE_POST_RESULT,obj是AsyncTaskResult对象。AsyncTaskResult中封装了AsyncTask的引用,Result对象,至于为何要如此封装后面会给出解释。
第5行,发送消息,将任务的执行切换InternalHandler所在的线程。
查看InternalHandler源码:
private static final InternalHandler sHandler = new InternalHandler();
private static class InternalHandler extends Handler {
@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;
}
}
}
第1行,sHandler是一个静态类型的变量,执行完耗时操作后要操作UI,sHandler只能在主线程中创建,因此AsyncTask类在主线程中被加载。
第3行,InternalHandler是Handler的子类,它是AsyncTask中的静态内部类,在类加载的时候就完成了。
第7行,取出Message中的数据obj,是一个AsyncTaskResult类型的对象,AsyncTaskResult也是AsyncTask的一个静态内部类。
第11行,调用AsyncTaskResult的字段mTask获取AsyncTask的实例,并调用finish方法,此时在主线程中执行。这里必须要通过AsyncTaskResult这个静态内部类来获取,若直接使用this,由于类加载时this没有值,会编译出错。因此,需要采用AsyncTaskResult这个静态内部类,来封装AsyncTask的引用。
查看AsyncTask$finish方法源码:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
第3行,如果任务被取消,也就是调用cancel方法,则onCancelled被调用。我们可以重写onCancelled方法,把任务取消后想做操作放在里面。
第5行,调用onPostExecute方法,它是在doInBackground方法之后被执行。我们可以重写onPostExecute方法,将操作UI的代码放在该方法里。
第7行,将变量mStatus的值设置为FINISHED。
四,最后
关于调用publishProgress方法后,onProgressUpdate方法被调用的原因就不再分析了,比较容易分析,有兴趣哥们可以自行验证。
AsyncTask中封装了两个线程池和Handler,自定义的线程池SerialExecutor保证多个任务的串行执行,真正执行异步任务的是线程池THREAD_POOL_EXECUTOR。
Android3.0之后,若需要并行的方式执行任务,可以直接调用AsyncTask$executeOnExecutor(THREAD_POOL_EXECUTOR,params)。将任务的执行交给线程池THREAD_POOL_EXECUTOR,而不再需要线程池SerialExecutor。关于THREAD_POOL_EXECUTOR,前面已经做了详细分析了。
到这里,对AsyncTask工作原理的分析就结束了~ ^_^