博主的上篇文章Android进阶系列9-Android异步消息处理Handler机制解析中介绍了Android异步消息处理机制相关的Handler、MessageQueue、Looper。今天再来看一个简便的在子线程切换到UI线程的官方类AsyncTask,借此加深下对异步消息处理的理解。
插播一段,博主至今写了十几篇博文,看了几十篇甚至上百篇博文,从怎样更容易理解的角度看,比较好的方式是从用法开始,一步步地去追踪内部调用过程,这样理解起来清晰易懂。郭婶是这方面的杰出代表,博主又一次在博文中夸郭婶了。。。
本文的诞生,要感谢这几篇文章 Android AsyncTask完全解析,带你从源码的角度彻底理解,Android源码分析—带你认识不一样的AsyncTask, Android异步任务处理框架AsyncTask源码分析 。
本文分为两部分:从调用的角度一步步地看代码;从使用注意点说开去。
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);
}
}
};
}
初始化了mWorker和mFuture变量,并在初始化mFuture的时候将mWorker作为参数传入。mWorker是一个Callable对象,mFuture是一个FutureTask对象(Callable和FutureTask的含义在后续的文章中,博主再做详细解释),这两个变量在后续调用中会用到,稍后再看。接下来就是execute()方法:
/*调用一个可变参方法*/
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
/*executeOnExecutor方法*/
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
/*这段if判断使得一个AsyncTask对象只能execute()一次,否则会抛出相应的异常*/
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方法*/
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
启动一个异步任务后,从AsyncTask的execute方法调用executeOnExecutor方法,executeOnExecutor的exec.execute(mFuture)方法是执行的关键(其他代码的解释见注释),看下它涉及到的代码,exec变量源于sDefaultExecutor,sDefaultExecutor 是SerialExecutor对象,SerialExecutor的execute方法如下:
......
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
......
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
......
public synchronized void execute(final Runnable r) {
/*将新的AsyncTask任务加入到双向队列中*/
mTasks.offer(new Runnable() {
public void run() {
try {
/*执行AsyncTask任务*/
r.run();//FutureTask类的run()方法
} finally {
/*当前AsyncTask任务执行完毕后,进行下一轮执行,如果还有未执行任务的话*/
scheduleNext();
}
}
});
/*如果当前没有任务在执行,直接进入执行逻辑*/
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
//从任务队列中取出队列头部的任务,如果有就交给并发线程池去执行
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
......
/*并发池初始化*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
SerialExecutor的execute方法调用scheduleNext方法获取任务,交由并发池执行。任务的具体的内容在r.run()方法内部。FutureTask的run方法源码如下:
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;//c由callable赋值
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();//run方法中执行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);
}
}
/*callable初始化于FutureTask构造函数*/
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;//由AsyncTask构造函数可以知道callable由mWorker赋值
this.state = NEW; // ensure visibility of callable
}
结合注释知道,c.call()方法是mWorker的call方法,它通过return postResult(doInBackground(mParams)); 调用了doInBackgroud方法,说明AsyncTask的doInBackgroud方法确实在子线程中执行。想必调用doInBackgroud的postResult方法应该就是把子线程的执行结果发送到主线程中。
private Result postResult(Result result) {
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
这段代码很亲切了,sHandler创建一个消息并发送给了MessageQueue。看下sHandler会如何处理这个msg:
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;
}
}
}
很容易看出来:doInBackgroud之后会结束任务,否则调用onProgressUpdate刷新。看下finish代码:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
如果是取消任务,就调用onCancelled方法,否则调用onPostExecute方法。而handleMessage中的MESSAGE_POST_PROGRESS条件出发在publishProgress方法中:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
同样的,利用sHandler实现子线程切换到主线程执行。至此我们知道doInBackgroud的结果如何传回UI线程以及publishProgress方法如何在自线程中操纵UI线程。借助了简单的Handler机制,封装了一套简单易用的代码给了开发者。
AsyncTask的执行示意图如下:
AsyncTask使用注意点
可能有人会想,AsyncTask这么好,有没有啥缺点呢?嗯,good question。从上文的代码分析中,不知道大家有没有注意到这几个问题:
- SerialExecutor是利用scheduleNext方法顺序获取任务再执行的,当有多个任务时,AsyncTask无法并行执行。
- SerialExecutor添加于Android 3.0之后,早期的AnsycTask存在一个问题:AnsycTask建立一个同一时刻能够运行的线程数为5,线程池总大小为128的线程池。也就是说当我们启动10个任务时,只有5个任务能够立刻执行,另外的5个任务则需要等待,当有一个任务执行完毕后,第6个任务才会启动,以此类推。而线程池中最大能存放的线程数是128个,如果尝试去添加第129个任务,程序就会崩溃。
- SerialExecutor是默认线程池,但可以在executeOnExecutor方法调用时,重新初始化exec变量,比如:
(new AsyncTask()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
这样就可以做到真正的并发执行AysncTask任务,而不是挨个任务取出来扔到并发池里。
总结
使用AsyncTask的规则(这些都在AsyncTask的注释文档中可以看到),或者大家可以参考 Android源码分析—带你认识不一样的AsyncTask这篇文章的注释。
AsyncTask的类必须在UI线程加载(从4.1开始系统会帮我们自动完成)
AsyncTask对象必须在UI线程创建
execute方法必须在UI线程调用
不要在你的程序中去直接调用onPreExecute(), onPostExecute, doInBackground, onProgressUpdate方法
一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常
AsyncTask不是被设计为处理耗时操作的,耗时上限为几秒钟,如果要做长耗时操作,强烈建议你使用Executor,ThreadPoolExecutor以及FutureTask
在1.6之前,AsyncTask是串行执行任务的,1.6的时候AsyncTask开始采用线程池里处理并行任务,但是从3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程来串行执行任务
很惭愧,做了一点微小的贡献!