1. 概述
AsyncTask是一种轻量的异步任务类,它中间封装了线程池和Handler,所以我们使用它可以更方便地执行后台线程操作和UI更新的切换。
2. 用法
AsyncTask是一个抽象的泛型类,它提供了Params、Progress、Result这三个泛型参数,其中Params是异步任务类参数的类型,Progress表示任务执行的进度类型,Result表示后台任务返回的结果类型。它有4个方法。
- onPreExecute(),在执行任务之前的操作,运行在ui线程上。
- doInBackground(),要执行的任务,运行在线程上。
- onProgressUpdate(),进度更新,运行在ui线程上,需要在doInBackground手动调用publishProgress方法。
- onPostExecute(),执行任务之后调用,运行在ui线程上。
3. 源码解析
3.1 线程池之ThreadPoolExecutor
在源码中可以看到这样一组变量定义:
private static final
// 手机CPU数量
int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// 线程池中核心线程数量的大小
int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
// 线程池中最大线程数量
int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
int KEEP_ALIVE_SECONDS = 30;
ThreadFactory sThreadFactory = new ThreadFactory() {};
这上面所有的参数它们有一个共同的作用,就是利用这些参数实例化一个线程池
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
sPoolWorkQueue,
sThreadFactory);
通过public static final的定义我们得知AsyncTask内部维护了一个静态线程池,默认情况下,AsyncTask的任务就是交给这个线程池来执行的。
从上面分析我们能知道AsyncTask内部维护了一个THREAD_POOL_EXECUTOR的线程池。
3.2 Executor接口的实现SerialExecutor
SerialExecutor是一个实现Executor的AsyncTask的静态内部类
private static class SerialExecutor implements Executor {
// 一个双端队列,用来存放任务Rnnable类
ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
// 当前正在执行的Runnable
Runnable mActive;
// 我们调用SerialExecutor的execute()会将Runnable再次封装,将其放入到双端队列mTasks的最后面。判断mActive是否为null来判断当前是否有任务在执行,如果没有任务在执行那么从任务队列中去一个任务去执行,如果有任务在执行则等待这个任务执行完毕后在finally中去取下一个任务
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
// mTasks取出一个Runnable将其交给mActive,然后再交由THREAD_POOL_EXECUTOR线程池来执行
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
从上面的代码分析中我们可以知道,当mTasks中的runnable作为参数传递给THREAD_POOL_EXECUTOR执行execute方法时,会在线程池的工作线程中执行匿名内部类Runnable中的try-finally代码段,即在工作线程中执行.run()方法,可以看到不管是正常执行还是抛出异常最终都会调用scheduleNext()方法,用来继续将mTasks中的下一个runnable传递出去,所以我们能到处结论,SerialExecutor是一个一个执行任务的是串行执行而非并行执行的。
另外,SerialExecutor将mTasks中的Runnable交给了THREAD_POOL_EXECUTOR去执行,说明SerialExecutor中的任务实际上是由THREAD_POOL_EXECUTOR线程池来处理的。
3.3 AsyncTask定义的字段
AsyncTask中定义的字段有这些
// 一个静态内部类,它绑定了UI线程
private static InternalHandler sHandler;
// 通过handler发布result的message code
private static final int MESSAGE_POST_RESULT = 0x1;
// 通过handler发布progress的message code
private static final int MESSAGE_POST_PROGRESS = 0x2;
// AsyncTask默认使用SERIAL_EXECUTOR作为它的Executor,所以默认情况下AsyncTask是串行而非并行执行的
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
// 是一个实现了Callable接口的对象
private final WorkerRunnable<Params, Result> mWorker;
// Future利用mWorker实例化
private final FutureTask<Result> mFuture;
// AsyncTask默认是未开始状态
private volatile Status mStatus = Status.PENDING;
// 任务是否被取消的标识
private final AtomicBoolean mCancelled = new AtomicBoolean();
// 任务是否真正开始了的标识
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
3.3.1 表示状态的枚举类型
AsyncTask内部定义了一个枚举类型用来表示任务执行的状态。
// 分别表示还没有执行任务、正在执行任务、任务结束
public enum Status {
PENDING, RUNNING, FINISHED,
}
AsyncTask的状态一定是这样变化的:PENDING->RUNNING->FINISHED。
3.3.2 默认线程池
sDefaultExecutor的初始值为SERIAL_EXECUTOR,利用我们对SERIAL_EXECUTOR的理解我们知道AsyncTask默认是串行执行的。
3.3.3 InternalHandler
我们之前在提到AsyncTask的概念时说过,AsyncTask封装了线程池和Handler,这个sHandler就是AsyncTask的静态内部类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;
}
}
}
我们通过InternalHandler构造函数可以知道,sHandler和ui线程上的Looper相关联所以说明sHandler绑定了ui线程。
3.3.4 mWorker和mFuture
mWorker是一个WorkerRunnable的类,WorkerRunnable是实现Callable接口的抽闲类,Callable和Runnable相似,Runnable内部定义了run方法,Callable内部定义了call方法,call方法可以有返回值,但run方法不能有返回值。
mFuture是FutureTask的对象,由于Executor的execute方法需要接收Runnable对象,但是再执行完任务之后我们又需要返回result,这是变利用了FutureTask对象,它同时实现了Callable和Runnable接口,它的构造函数需要传入一个Callable对象,所以我们也可以将FutureTask对象mFuture传递给Executor的execute方法去执行。
当任务执行完之后会调用FutureTask的done方法,在任务执行的过程中还可以随时调用FutureTask的cancel方法取消执行任务,取消任务之后仍然会调用done方法。
3.4 AsyncTask的构造函数
AsyncTask的构造函数实际上就是对mWorker和mFuture的实例化。
mWorker的实例化
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
// 将任务开始标识设为true
mTaskInvoked.set(true);
Result result = null;
// 将call方法设置为后台线程级别
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 在线程池中执行doInBackground()方法,并返回result
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
// 将结果交由postResult()方法
postResult(result);
}
return result;
}
};
mFuture的实例化
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);
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
mWorker其实是一个Callable类型的对象。实例化mWorker,实现了Callable接口的call方法。call方法是在线程池的某个线程中执行的,而不是运行在主线程中。在线程池的工作线程中执行doInBackground方法,执行实际的任务,并返回结果。当doInBackground执行完毕后,将执行完的结果传递给postResult方法。postResult方法我们后面会再讲解。
mFuture是一个FutureTask类型的对象,用mWorker作为参数实例化了mFuture。在这里,其实现了FutureTask的done方法,我们之前提到,当FutureTask的任务执行完成或任务取消的时候会执行FutureTask的done方法。done方法里面的逻辑我们稍后再将。
3.5 AsyncTask.execute()
我们来看一下execute是如何运作的
3.5.1 executeOnExecutor()
在实例化AsyncTask之后需要调用AsyncTask的execute方法来执行任务
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
可以看到实际上execute调用了executeOnExecutor方法。
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("");
case FINISHED:
throw new IllegalStateException("");
}
}
// 在开始执行任务之前将状态改变
mStatus = Status.RUNNING;
// 真正执行任务之前调用该方法
onPreExecute();
//
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
mStatus状态如果不是PENDING就抛出异常,说明AsyncTask只可以执行一次任务。
因为@MainThread我们知道executeOnExecutor()方法是运行在ui线程上的,所以onPreExecute()也是运行在ui线程上的。
再之后,调用了exec.execute(mFuture),开始执行任务,由于exec默认是SERIAL_EXECUTOR,所以会将包含任务的mFuture放入到静态队列中去,根据SERIAL_EXECUTOR的队则去排队执行任务。
开始执行任务之后,会调用mWorker中的call方法,从而执行doInBackground()方法之后,会将执行结果传递给postResult()方法。
3.5.2 postResult()
postResult()方法的代码如下
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
它会通过sHandler来发送带有Result的信息给ui线程,这其中出现了AsyncTaskResult,它是什么?
3.5.3 AsyncTaskResult
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
mTask表明了它是哪个AsyncTask的结果,mData表示存储的数据
3.5.4 sHandler的handleMessage()
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;
}
}
执行result.mTask.finish()方法,就是执行当前AsyncTask的finish方法
3.5.5 finish()
private void finish(Result result) {
if (isCancelled()) {
//如果任务被取消了,那么执行onCancelled方法
onCancelled(result);
} else {
//将结果发传递给onPostExecute方法
onPostExecute(result);
}
//最后将AsyncTask的状态设置为完成状态
mStatus = Status.FINISHED;
}
finish方法一目了然,如果任务被取消了则执行onCancelled()方法,如果没有则执行onPostExecute()方法,因为sHandler是和ui线程关联的,所以这两个方法都运行在ui线程中。
3.5.6 done()
在mWorker的call()执行完毕之后,会调用mFuture的done()方法,我们查看源码知道不管任务是正常执行完毕还是被取消都会调用postResultIfNotInvoked()方法。
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
//只有mWorker的call没有被调用才会执行postResult方法
postResult(result);
}
}
我们在mWorker的call方法中将mTaskInvoked设为true,所以如果mWorker的call方法没有被执行则会调用postResult()方法。
如果AsyncTask正常执行完成的时候,call方法都执行完了,mTaskInvoked设置为true,并且在call方法中最后执行了postResult方法,然后进入mFuture的done方法,然后进入postResultIfNotInvoked方法,由于mTaskInvoked已经执行,所以不会执行再执行postResult方法。
如果在调用了AsyncTask的execute方法后立马就执行了AsyncTask的cancel方法(实际执行mFuture的cancel方法),那么会执行done方法,且捕获到CancellationException异常,从而执行语句postResultIfNotInvoked(null),由于此时还没有来得及执行mWorker的call方法,所以mTaskInvoked还未false,这样就可以把null传递给postResult方法。
由此AsyncTask的细节应该都清楚了。
4. 加深理解
4.1 例子
我们知道串行执行任务调用execute(),并发执行时调用executeOnExecutor()方法。
调用execute时,默认会将sDefaultExecutor设置为SERIAL_EXECUTOR,SERIAL_EXECUTOR是一个串行执行的过程。
调用executeOnExecutor(sDefaultExecutor, xx),我们将sDefaultExecutor变量设置为我们传递进去的线程池,或者直接使用AsyncTask内部维护的线程池ThreadPoolExecutor,这样任务的传递给ThreadPoolExecutor的execute()方法,它的方法则是一个并行执行的过程。
为了验证这个结论,我们写下了这几个测试的伪代码
class MyAsyncTask01 extends AsyncTask<String, Object, String>{
@Override
protected void onPreExecute() {
Log.i(Tag, "01-onPreExecute");
super.onPreExecute();
}
@Override
protected String doInBackground(String... params) {
for ( String string : params ){
Log.i(Tag, "01-doInBackground:" + string);
Thread.sleep(5000);
}
return "完成";
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.i(Tag, "01-onPostExecute:" + s);
}
}
class MyAsyncTask02 extends AsyncTask<String, Object, String>{
@Override
protected void onPreExecute() {
Log.i(Tag, "02-onPreExecute");
super.onPreExecute();
}
@Override
protected String doInBackground(String... params) {
for ( String string : params ){
Log.i(Tag, "02-doInBackground:" + string);
}
return "完成";
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.i(Tag, "02-onPostExecute:" + s);
}
}
MyAsyncTask01 myTask01 = new MyAsyncTask01();
MyAsyncTask02 myTask02 = new MyAsyncTask02();
运行:
// 第1组
myTask01.execute("a");
myTask02.execute("b");
// 第2组
myTask01.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "a");
myTask02.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "b");
第1组
因为它是串行执行的,所以等待任务1执行完毕后才会执行任务2。
第2组
它是并行执行的,所以任务1和任务2是同时执行的。
4.2 doInbackground()竟然没有执行
我们知道并行执行要调用executeOnExecutor()方法,但是我的doInbackgroud方法竟然没有执行或者是等了很久才执行,其实原因这样的。AsyncTask内部维护了一个静态线程池ThreadPoolExecutor,因为它是静态的,所以它内部管理的不仅仅是我们定义AsyncTask任务的线程,它还有可能管理其他任务的线程,假如app后台运行了很多下载任务,任务都是利用AsyncTask并发下载的,ThreadPoolExecutor线程池中没有空闲线程了,这时候我们开启一个异步任务AsyncTask,由于没有空闲线程,所以你的doInbackground()方法需要等待有线程空闲下来才开始执行,才造成了以上的情况。我们可以模拟一下这个情况。
for (int i=0; i<100; i++){
MyAsyncTask01 t1 = new MyAsyncTask01();
t1.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "a");
}
MyAsyncTask02 t2 = new MyAsyncTask02();
t2.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "b");
由图可见,足足过了25秒,t2任务才开始执行。这时候我们应该讲线程池换成我们自定义的线程池。