Android开发知识 onGithub
说到AsyncTask,它几乎能够用最简单的方式将操作异步执行,再呈现给UI线程。你不需要自己写一个线程,然后通过Handler
去将结果返回给UI线程。只要简单的重写 onPreExecute
,doInBackground
,onProgressUpdate
,onPostExecute
四个方法,然后调用execute
方法,是不是超级简单。
可是,你了解AsyncTask是如何操作你的任务的吗?它是如何封装Handler将异步任务执行结果返回给UI线程的?使用AsyncTask有哪些需要注意的?本文从源码分析AsyncTask的工作原理,部分内容来自源码。
1.任务执行方式
目前的AsyncTask默认的任务处理是在单线程中顺序执行,之前有过一段时间可以在线程池中执行,不信你看execute
方法的注释:
我简单的翻译一下,execute
方法将队列中的任务在一个后台的单线程或线程池中执行。AsyncTask的第一个版本是顺序执行。在1.6(DONUT)版本后,改成多任务的线程池中执行。但在3.2(HONEYCOMB)后,为了避免一些线程同步的错误,又改回在单线程中执行。如果想在线程池中执行,可以这样:
new AsynTask().executeOn(AsyncTask.THREAD_POOL_EXECUTOR,"")
复制代码
显然这种方法,并不建议。
既然说到AsyncTask.THREAD_POOL_EXECUTOR
,它是什么呢?
public static final Executor THREAD_POOL_EXECUTOR =
new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
sPoolWorkQueue,
sThreadFactory);
复制代码
THREAD_POOL_EXECUTOR是一个线程池的执行器(有关线程池的可以参考 这篇文章。在这里你只要了解它是一个核心线程数量是CPU数+1,最大线程数量是2*CPU数量+1就可以了。
SerialExecutor
话说回来,单线程顺序执行是如何执行的?请看:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
复制代码
这个sDefaultExecutor
是AsyncTask任务执行器。看过源码你会发现有这样一个方法:
/** @hide */
public static void setDefaultExecutor(Executor exec) {
sDefaultExecutor = exec;
}
复制代码
sDefaultExecutor
是可以设置的,只不过你调用不了,被隐藏了(@hide)。
那么SERIAL_EXECUTOR
是什么呢?它是一个SerialExecutor
的实例。
可以看到,在execute
中会调用offer
方法会将Runnable r
包装一下放到ArrayDeque
队列里,包装的新Runnable
保证原来的Runnable
执行之后会去取队列里的下一个Runnable
,从而不会导致中断。 scheduleNext
做了什么呢?可以看到scheduleNext
是从队列中取出Runnable
然后交给THREAD_POOL_EXECUTOR
执行。也就是说SerialExecutor
只是将任务按先后顺序排列到队列中,真正执行任务的是THREAD_POOL_EXECUTOR
。
2.任务执行过程
在你调用execute
的时候会这样:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
复制代码
你看,最后还是会调用到executeOnExecutor
,默认传了一个SERIAL_EXECUTOR
。并且,看见那个@MainThread
了吧,execute
一定在主线程调用。
请看executeOnExecutor
:
每个AsyncTask都有一个Status,代表这个AsyncTask的状态,Status是一个枚举变量,每一个状态在这个Task的生命周期里赋值一次,也就是这个Task一定会经历 PENDING -> RUNNING -> FINISHED
的过程。 PENDING代表Task还没有被执行,RUNNING代表当前任务正在执行,FINISHED代表的是onPostExecute
方法已经执行完了,而不是doInBackground
。
/**
* Indicates the current status of the task. Each status will be set only once
* during the lifetime of a task.
*/
public enum Status {
PENDING,
RUNNING,
FINISHED,
}
复制代码
话说回到executeOnExecutor
中,如果当前的Task的状态不是PENDING,那么就会抛出异常。也就是同一个Task,你只能execute
一次,直到它的异步任务执行完成,你才可以再次调用他的execute
方法,否则一定会报错。 然后调用onPreExecute
方法,之后会提交给SERIAL_EXECUTOR
执行。但是这个mWorker
是什么?mFuture
是什么?
mWorker
mWorker
是WorkerRunnable
的具体实现,实现Callable
接口,相当于一个能够保存参数,返回结果的Runnable
。
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
复制代码
当你创建一个AsyncTask的时候就会创建mWorker
和mFutureTask
。
可以看到,mWorker
的call
方法主要的工作是设置call
是否被调用,调用你重写的doInBackground
方法,获得Result
(这个Result
的类型就是你声明AsyncTask时传入的类型),再将Result
调用postResult
方法返回。关于postResult
请往下看。
mFuture
可以看到mFuture
中有一个postResultIfNotInvoked(get());
方法,通过get方法获得mWorker
的执行结果,然后调用postResultIfNotInvoked
方法,由于某些原因,mWorker
的call
可能没有执行,所以在postResultIfNotInvoked
中能够保证postResult
一定会执行一次,要不在mWorker
的call
中执行,要不在postResultIfNotInvoked
中执行。
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
复制代码
那么这个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
实际上是获得了一个AsyncTask内部的一个Handler
,将result
包装在AsyncTaskResult
中,并将它放在message发送给Handler。
那么AsyncTaskResult
是如何封装的?
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
复制代码
可以看到包含AsyncTask的实例(mTask)和数据(mData)。当将任务执行的结果返回时,mData保存的是Result,当更新进度的时候mData保存的是和Progress
类型一样的数据。你可以往下看。
获取执行结果和更新执行的进度
先说一说Handler
。
每个AsyncTask都会获得一个InternalHandler
的实例。可以看到,InternalHandler
绑定到了主线程的Looper
中(关于Looper与Handler的关系,可以参考这篇文章,所以你在异步线程中执行的结果最终都可以通过InternalHandler
交给主线程处理。再看handlerMessage
方法,获得AsyncTaskResult
对象,如果传的是MESSAGE_POST_RESULT
类型,就调用AsyncTask的finish
方法(别忘了result.mTask
其实就是当前的AsyncTask)。
finish
做了什么?
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
复制代码
可以看到,判断你是否取消了任务,取消则优先执行onCancelled
回调,否则执行onPostExecute
,并更改Task的状态。
如果是一个MESSAGE_POST_PROGRESS
,就会执行onProgressUpdate
方法。那MESSAGE_POST_PROGRESS
的信息是谁去发送的呢?请看:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
复制代码
也就是当你调用publishProgress
的时候,会将传递的values
包装成AsyncTaskResult
,AsyncTaskResult
的mData会保存进度的数据,将message发送给handler。
有个方法需要说明一下,就是cancle
方法。
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
复制代码
作用是设置被取消的状态,然后取消FutureTask
的执行。当task已经执行完了,或已经被取消,或因为某些原因不能被取消,会返回false。如果任务已经执行,那么根据mayInterruptIfRunning
决定是否打断(interrupt)当前正在执行Task的线程。 调用这个方法会在doInBackground
返回后回调onCancelled
方法,并且onPostExecute
不会执行,所以当你需要取消Task的时候记得在doInBackground
通过isCancelled
检查返回值。
注意事项
1. 由于AsyncTask是单线程顺序执行的,所以不要用AsyncTask执行耗时太久的操作,如果有很多耗时太久的线程,最好使用线程池。
2. onPreExecute
、onProgressUpdate
、onPostExecute
都是在UI线程调用的,doInBackground
在后台线程执行。
3. 调用cancel
方法取消任务执行,这个时候onPostExecute
就不会执行了,取而代之的是cancel
方法,所以为了尽快的退出任务的执行,在doInBackground
中调用isCancelled
检查是否取消的状态。
4. 其他
- AsyncTask类一定要在主线程加载
- AsyncTask类的实例一定在主线程创建
execute
方法一定在主线程调用- 不要主动调用
onPreExecute
等方法 - 任务只能在完成前执行一次。