在上一篇博客中,我们提到AsyncTask的任务默认是串行执行的,并且AsyncTask对象必须在主线程中创建,并且一个AsyncTask对象只能执行一次execute方法,否则就会报错,这一切的原因是什么呢?本文就将通过源码分析来解释这几个问题。
首先来再看一下AsyncTask的几个核心方法:
- excute: 创建好AsyncTask对象后,需要调用此方法才开始执行任务;
- onPreExecute:在主线程中执行,在异步任务执行之前执行,可以做一些准备工作;
- doInBackground:在线程池中执行,执行具体的异步任务。在这个方法中可以调用publishProgress方法来更新任务的进度;
- onProgressUpdate:在主线程中执行,调用publishProgress方法后会被调用;
- onPostExecute:在主线程中执行,异步任务执行完毕后会被调用,返回执行结果;
- onCancelled:在主线程中执行,异步任务被取消时会被调用,这样onPostExecute方法将不会被调用;
以上方法中,只有doInBackground方法是在线程池中去执行的,毕竟耗时操作也是在这个方法中去完成的。
1、AsyncTask的使用
这里使用了前文所用到的例子,只是做一个演示, 示例代码如下:
private class MyTask extends AsyncTask<Params, Progress, Result> {
....
// 作用:执行 线程任务前的操作
@Override
protected void onPreExecute() {
...
}
// 作用:接收输入参数、执行任务中的耗时操作、返回 线程任务执行的结果
@Override
protected String doInBackground(String... params) {
...
// 可调用publishProgress()显示进度, 之后将执行onProgressUpdate()
publishProgress(count);
}
// 作用:在主线程 显示线程任务执行的进度
@Override
protected void onProgressUpdate(Integer... progresses) {
...
}
// 作用:接收线程任务执行结果、将执行结果显示到UI组件
@Override
protected void onPostExecute(String result) {
...// UI操作
}
// 作用:将异步任务设置为:取消状态
@Override
protected void onCancelled() {
...
}
}
MyTask mTask = new MyTask();
mTask.execute("传入参数");
...
2、源码分析
通过上述事例代码,我们发现,整个AsyncTask的工作是从调用execute方法开始的,所以从这里开始追踪代码,execute源码如下:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
看注解,已经标注了需要在主线程中去执行execute方法,这里的源码只有一行,但需要分成两步去看:
- 1、调用了 executeOnExecutor方法;
- 2、传入了sDefaultExecutor这个线程池;
2.1 sDefaultExecutor线程池
这是一个串行的线程池,我们**整个进程的所有AsyncTask任务全部都在这个串行的线程池中去执行**,所以说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() {//此处通过offer方法添加到队列中,并没有立即执行
public void run() {
try {
r.run();//1、执行当前任务
} finally {
scheduleNext(); //2、执行完任务后,取出下一个任务继续执行, 这一段代码是同步的,所以只能顺序执行
}
}
});
if (mActive == null) {
scheduleNext();//如果当前没有任务在执行,则去取出任务执行
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);//通过THREAD_POOL_EXECUTOR线程池去执行任务
}
}
}
从源码可看到,这个线程池的execute是用synchronize加锁了的,所以一次也只能执行一个任务, 所有的任务都存储在mTask这个双端队列中,按照先入先出来执行。当来了一个新任务后,直接通过offer添加到mTasks中, 等同前面先加入的任务执行完后在执行,如果当前没有任务执行,则直接调用scheduleNext()开始去执行。并且,真正的执行是通过线程池THREAD_POOL_EXECUTOR去完成的, 前一个线程池的作用只是用来保存了任务及任务的排队。
以下是THREAD_POOL_EXECUTOR的初始化代码,比较简单:
public static final Executor THREAD_POOL_EXECUTOR;
static {//静态代码块直接创建了一个线程池,没啥好讲的
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
2.2 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(); //此处在执行任务前,去调用了onPreExecute方法
mWorker.mParams = params;//把传入进来的参数进去保存
exec.execute(mFuture);//mFeature就是根据params创建的一个FeatureTask
return this;
}
首先判断了当前的状态如果不是pending,则表示此任务已经在运行或者是运行完毕,这种情况下直接抛出异常, 也就解释为什么execute方法只能运行一次。
然后用exec这个线程池,也就是上面传入的线程池,去执行mFeature这个任务,这里对mWorker、mParams、mFeature参数解释一下:
- mWorker:是一个Callable对象,和Runnable比较相似,不过Callable执行后会有返回值,而Runnable没有返回值,都是多线程常用对象;
- mParams: 我们传入到AsyncTask中的参数;
- mFeature:是一个FeatureTask对象,也是多线程中常用对象,用来保存mWorker这个Callable对象,并且标注它的执行状态;
通过以上分析,其实在这里 exec.execute(mFuture);任务就处于执行或等待执行了。
我们重写的AsyncTask的doInBackGroud方法呢?怎么没有看见??? 其实都在构造方法里面。
2.3 AsyncTask的构造方法
前面的梳理是从execute开始的,其实在此方法前,我们最先完成的是new AsyncTask对象,所以最开始执行的是构造方法, 只是为了更好理解逻辑,把构造方法放到了这里分析, 源码如下:
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);//1、当此任务被调用时,这里设置一个true的标记
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);//2、调用doInBackground方法
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result); //3、把结果切回主线程
}
return result;
}
};
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);
}
}
};
}
源码注释写到,构造方法需要在主线程调用, 然后在构造方法里面,主要就创建了两个对象mWorker和mFuture, 这两个对象已经在前面介绍了, 不过这里需要注意的是当 mWorker被执行时,实际上就是去调用了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;
}
这里主要是通过getHandler()方法去获取到AsyncTask内部创建的Handler对象,然后发送到主线程的消息队列, getHandler得到的对象源码如下:
private static InternalHandler sHandler;//注释1、 这是一个静态对象
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
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);//注释2 、回调onProgressUpdate
break;
}
}
}
从源码可以看到,如果是执行完了,则会回调onProgressUpdate方法, 这里,需要非常注意一点,sHandler对象是一个静态对象,因此,一旦在一个进程中完成初始化,就不会再进行初始化了, 另外,根据Handler消息机制的原理,Handler在主线程创建才会和主线程的Looper和MessageQueue进行绑定,才能把消息发往主线程的消息队列,不明白的可以参考另一篇博客–Android消息机制详解:Handler、MessageQueue、Looper 所以可以得出: AsyncTask对象必须在主线程中创建使用。
3、为什么AsyncTask要设计为两个线程池?
AsyncTask在Android3.0以后,默认是串行执行的,其实AsyncTask本身也可以提供并行执行的功能,方法就是executeOnExecutor, 只是一般我们不会用到,也不建议这么去使用。如果使用executeOnExecutor方法,则任务的执行会直接交给THREAD_POOL_EXECUTOR这个线程池来执行,而不需要使用默认线程池sDefaultExecutor。 从这里也可以看出,其实Google在这里设计两个线程池,就是为了兼容以前老版本设计和新版本设计,同时保留并行的功能,这就是AsyncTask设计为两个线程池的原因。
4、其他知识点
以上基本就完成了对AsyncTask的主要部分的梳理,剩下的onPostExecute和onCancelled两个方法没分析,其实在上述的基础上去看,就会比较简单了,如果感兴趣,可以去了解一下。