AsyncTask一直是用的比较频繁的一个类,之前有时间也看了下其实现的源码,都是一知半解,对其如何加入线程池,在线程池中如何执行任务还有点没搞清楚,今天再次阅读源码,终于是有种豁然开朗的感觉,于是赶紧趁热记录一下。
首先,简单理一下AsyncTask的使用流程:
比如在Activity的onCreate中新建一个AsyncTask对象,然后执行它的execute()方法,一般会去使用静态内部类的方式来规避内存泄露,比如这样写:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AsyncTask task = new MyTask(this);
task.execute();
}
MyTask是一个继承自AsyncTask的静态内部类,一般需要实现几个重要的方法:onPreExecute、doInBackground、onPostExecute、onProgressUpdate
比如这样:
static class MyTask extends AsyncTask<String,Integer,String>{
private WeakReference<Activity> weakAty;
public MyTask(Activity activity){
weakAty = new WeakReference<Activity>(activity);
}
@Override
protected String doInBackground(String... params) {
for(int i=0;i<100;i++){
Log.i("Mytask","i="+i);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(isCancelled()){
break;
}
}
return null;
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.i("Mytask","执行结束了");
AsyncTaskActivity mActivity;
if((mActivity= (AsyncTaskActivity) weakAty.get())!=null){
mActivity.doSomething();
}
}
@Override
protected void onCancelled() {
super.onCancelled();
Log.i("Mytask","执行了取消");
}
}
具体这些方法的意义不在赘述,可查看相关的文档。
所以使用AsyncTask概括来说就是,我们要继承AsyncTask实现其方法,然后在使用的地方,创建一个对象,然后执行其execute()方法,那么具体它是怎么来工作的呢?
1、先看它的构造方法,毕竟这是第一个入口:(注:所有源码都是在API-23上分析的,版本不同个别函数实现有差别)
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(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的类型是WorkerRunnable,实际上是一个Callable对象,看:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
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接口,并定义了一个Params成员变量,这个成员变量就是用来存储我们在执行AsyncTask的execute()方法时传入的params。
至于为什么会起名叫xxxRunnable(刚开始阅读源码的时候可能会被这个名字误导),大概是因为它要被放到线程池里面执行吧,而线程池里面执行的就是runnable对象。当然这里并不是说直接把这个mWorker放进线程池,也不能,实际上它是由mFuture对象来包装的,作为FutureTask对象的一个成员变量。
我们来看看mFuture对象,它的类型是FutureTask,看:
public class FutureTask<V> implements RunnableFuture<V>
它实现了RunnableFuture接口,从字面意思看,可以猜测这个接口是有Runnable和Future的功能:
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
果不其然,这个接口实现了双继承,同时有了Runnable和Future的功能,Runable我们知道可以传入到线程池中,也就是说mFuture这个对象可以被传入到线程池中执行,而Future呢,它有一个重要的方法get(),可以在调用get的地方阻塞,直到有结果返回。我们知道,AsyncTask内部执行的时候是需要把执行结果返回的,返回的结果我们用来更新UI。但是Runnable对象只有一个run方法,执行完成就完了,并不会返回任何的执行结果,而Callable恰恰满足了这点,它有一个call方法,会返回一个泛型结果,这不正式我们需要的吗?所以AsyncTask同时使用了这三个接口的功能。
我们继续回到FutureTask对象上,看它的构造函数:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
传递了一个Callable对象进来,实际上就是AsyncTask构造里面创建的mWorker对象,FutureTask对象持有mWorker的引用,如此,这三个接口的功能就关联到一起了。
概括起来就是,线程池Executor加入的任务需要一个Runnable对象,在这里就是FutureTask来替代,而AsyncTask执行完成需要一个返回结果来通知UI,由于Runnable做不到,那就使用Callable来,于是就有了WorkerRunnable,而为了保证一定能得到一个结果,那就得使用阻塞的方式来保证一定可以得到一个结果,就需要使用Future的get,于是也就有了FutureTask。
对象创建完毕,接下来就要去执行任务了。我们在仔细回顾一下AsyncTask的构造方法,说明一个AsyncTask对象就只有一个mWorker和一个mFuture对象,请记住这一点。
2、执行任务
AsyncTask有两个执行任务的入口:
execute(Params... ); // 默认使用串行线程池方式执行
execute(Executor , Params... ); // 自定义线程池执行
实际上第一个方法最终还是调用了第二个方法,只不过使用了默认的串行线程池来执行。
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) { // 首先是判断状态,这个状态是个成员变量,一旦我们的AsyncTask对象执行了execute,这个状态就会被置为RUNNING,所以不能重复执行execute方法
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; // execute的参数被mWorker的成员变量记住
exec.execute(mFuture); // 使用线程池接口的execute方法执行,mFuture中包含了mWorker变量
return this;
}
看完注释后,我们来到了exec.execute(mFuture)这里,默认情况下这个exec对象就是sDefaultExecutor,接下来我们看这个对象又是什么鬼:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
它被定义为一个静态的,并且被初始化为SERIAL_EXECUTOR对象,SERIAL_EXECUTOR对象也是一个静态的并且是final类型,且在其前面初始化为一个SerialEexcutor对象,看名字可以知道是个串行执行任务。sDefaultExecutor这个作为静态的对象,说明在整个应用进程的生命周期内就只有这么一个对象,所以说,在一个应用进程内,我们可以创建多个AsyncTask对象,但是所有的AsyncTask对象共用这么一个SerialEexcutor对象。我们来看看这个SerialEexcutor对象做了什么:
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);
}
}
}
SerialEexcutor实现自Executor,实现了其方法execute,该方法传入一个Runnable对象(实际上就是我们在AsyncTask构造方法里面创建的mFuture对象),另外SerialExecutor提供了一个同步的scheduleNext方法,这个方法真正的把我们要执行的任务放到线程池中来执行。所以实际上SerialExecutor并不是一个线程池,因为它没有创建线程执行任务的能力,它只是简单的实现了Executor的方法,然后通过双端队列来把我们的任务按先进先出的序列存储起来,然后在按先后顺序取出来放到线程池中执行。我们来详细的分解一下这个过程。
首先我们看到在上面代码的scheduleNext方法中有一个静态成员变量THREAD_POOL_EXECUTOR,从字面意思我们可以知道这是一个真正的线程池,它是在SERIAL_EXECUTOR初始化前就已经初始化了的,它也是全应用内共享的,就这么一个对象。
/**
* 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);
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
我们暂且不关心这个线程池的定义,不了解的可搜索线程池的一些概念,我们只需要知道它能加入一个runnable任务,然后内部就会使用线程来执行这个任务,也就做到了异步后台执行。
了解了以上信息,我们开始从任务执行的起始点来分析一下这个整个流程。
还记得AsyncTask的执行步骤吗?不记得了可以往上翻一翻。
我们直接看executeOnExecutor方法,倒数第二行执行了这个:
exec.execute(mFuture);
这里的exec就是静态成员变量SERIAL_EXECUTOR,继续看SERIAL_EXECUTOR的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);
}
}
}
首先把我们传进来的mFuture对象包裹在一个新的对象Runnable中,并把这个新的Runnable对象加入到双端队列mTask的队尾:
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
然后继续执行,假如是第一次,那么mActive肯定为空,就会去执行scheduleNext方法:
if (mActive == null) {
scheduleNext();
}
scheduleNext方法从队列mTask的队头取出一个Runnable对象,如果不为空(因为我们在前面已经加入过,所以这里必然不为空),就把这个Runnable对象加入到线程池中去执行。注意,这里的Runnable对象(暂且用mRun来表示)是我们在加入mTask的时候创建的对象,包裹了我们真正要执行的任务mFuture。直到这里,我们还是在主线程中执行(前提是我们的AsyncTask的execute方法是在主线程中执行的)。我们先不管线程池中是如何执行那个新创建的mRun对象的,假如另外创建了一个AsyncTask对象,并执行其execute方法,那么会继续调用SERIAL_EXECUTOR的execute方法,接着在调用mTask的offer的时候,又新创建一个mRun对象,把这个mRun对象存储到mTask中,继续执行,由于我们第一次执行的时候,mActive已经被赋值了:
//execute中的方法,第一次执行为空会去执行scheduleNext
//第二次执行的时候mActivie不为空,不再去执行shceduleNext
if (mActive == null) {
scheduleNext();
}
//scheduleNext中的方法,第一次执行完成后,mActive不为空了
if ((mActive = mTasks.poll()) != null) // 这里执行了赋值
mActive对象不为空,不会执行后面的语句,也就是说再有新的AsyncTask任务创建执行,到这里都只会吧这些新的任务包裹进一个新的Runnable对象中,并存储在mTask这个队列里面。先进来的任务会放在靠近队头,后进来的依次排在队尾,就这么把任务串行的加进来了。
接着我们来看线程池是如何执行这些任务的。
假如第一次,scheduleNext肯定会被执行
if (mActive == null) {
scheduleNext();
}
mRun任务(即mActive对象)被加入到线程池中执行:
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
也就是会执行这个最外层的run方法:
new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
}
然后去执行我们传进来的r(即mFuture)的run方法,先不管run方法执行了什么。当执行完run方法后,会继续执行finally里面的方法,即执行了scheduleNext方法,这个方法就是去队列里面取任务执行,就这样递归的把mTask里面的任务一个个取出来执行了,当mTask空了的时候,这个递归就结束了。
我们可以看出,mActive是在线程池中的线程里执行的,而真正的任务就是r.run,所以mFuture的run是在子线程中执行的。
于是我们来看看mFuture的run执行了什么?直接看AsyncTask的构造:
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(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);
}
}
};
}
没看到run啊,那走进FutureTask去看看,在其里面我们看到了run方法:
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call(); // 首先去调用了mWorker的call方法,得到结果
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result); // 设置结果,使用get可以得到结果
}
} 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);
}
}
我们就只看正常情况,先调用了mFuture的成员变量mWorkder的call方法:
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // 设置线程优先级
//noinspection unchecked
Result result = doInBackground(mParams); // 执行AsyncTask的doInBackgroud方法,并取得结果
Binder.flushPendingCommands();
return postResult(result); // 执行AsyncTask的postResult返回结果
}
};
在mFuture的run方法里面执行了mWorker的call方法,call方法先调用了AsyncTask的doInBackground方法,取得执行的结果,然后用postResult返回结果。可以看到,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;
}
获取主线程的一个handler对象,把结果组装成一个AsyncTaskResult的对象发送到主线程中:
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);
break;
}
}
}
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
在handleMessage中调用AsyncTask的finish方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
进而会回调我们所熟知的那几个方法。
我们可以看到在mWorker的call方法里面实际上就跑完了整个流程,那么在AsyncTask的构造函数里面的mFuture对象创建的时候的done方式是什么作用呢?
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);
}
}
};
我猜想可能是做一些异常的收尾工作。我们回顾下在FutureTask的run方法里面有这么几句:
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
通过mWorker的call获得结果,如果出现异常,执行setException,如果获取结果成功执行set
/**
* Sets the result of this future to the given value unless
* this future has already been set or has been cancelled.
*
* <p>This method is invoked internally by the {@link #run} method
* upon successful completion of the computation.
*
* @param v the value
*/
protected void set(V v) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = v;
U.putOrderedInt(this, STATE, NORMAL); // final state
finishCompletion();
}
}
/**
* Causes this future to report an {@link ExecutionException}
* with the given throwable as its cause, unless this future has
* already been set or has been cancelled.
*
* <p>This method is invoked internally by the {@link #run} method
* upon failure of the computation.
*
* @param t the cause of failure
*/
protected void setException(Throwable t) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = t;
U.putOrderedInt(this, STATE, EXCEPTIONAL); // final state
finishCompletion();
}
}
最终这两个都执行了finishCompletion
/**
* Removes and signals all waiting threads, invokes done(), and
* nulls out callable.
*/
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (U.compareAndSwapObject(this, WAITERS, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
可以看到在这个方法里面执行了done,这个方法在FutureTask里面是个空实现,那么必然在其子类里面实现,也就是我们的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()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
这里的mTaskInvoked在我们的mWorker的call回调的时候已经被置为true了,所以这里就不会再去执行了postResult方法了,除非有了异常了,没有执行call回调。
至此,整个任务的处理流程就理清楚了。通过追根究底的分析,终于是对AsyncTask有个比较全面的认识了。之前大部分都是一知半解,面试的时候还会被问道,SerialExecutor真的是线程池吗这种问题,如果不是很理解,那么可能就认为这是个线程池,实际上它只是一个串行任务队列的处理类而已。另外有面试官还会问线程池是怎么创建线程执行任务的,这又是另外一个比较大的分析点了~回头有时间在细细研究下。
ps:近期对线程池有了进一步的理解,关于线程池如何创建线程执行任务的话题可查看: