我们知道,Android中的异步操作有几种,一般常见的是 new Thread + Handler,这种最常见;Activity 提供了 runOnUiThread(Runnable action) 方法;Android源生比较早的也提供了一个系统封装好的异步方法, AsyncTask ,传入参数,然后再指定的方法中做相应的操作。 AsyncTask 历经几次修改,后来基本都废弃不用了,但我还是想对它做一次源码分析,以24版本为例,分析一番。本文分析代码,它的用法网上一大堆,非本文的重点。
代码是个抽象类,意思是使用时,至少要重写 doInBackground() 方法;进入类里面,看到有个静态模版快,这个里面的代码会执行一遍,优先于类的对象的加载,看看里面的内容,是一个线程池,我们可以看看线程池的配置参数,private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); 这个意思是根据手机cpu获取最佳线程个数,private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); 这个是线程池的核心线程,意思是cpu支持个数减去1,然后和4做比较,获取其中较小的数,然后再和2做比较,取出其中比较大的数,作为线程池的核心线程; 非核心线程数为 cpu 最佳个数的2倍加1,;非核心线程回收的等待时间为30,单位为秒;线程队列为线性阻塞队列,最大值为128个;线程工厂方法里面产生线程直接new,名字为 AsyncTask #1开始,最后一个数字依次累加,如 AsyncTask #2、AsyncTask #3 。
public enum Status {
PENDING,
RUNNING,
FINISHED,
}
这是一个枚举,里面代表三种状态:准备,进行中,结束。这是一个标识记录。getHandler() 方法,是获取一个Handler,我们发现,InternalHandler 重写了 Handler,构造器里面,传入的Looper是 Looper.getMainLooper(),意味着这个Handler是个UI线程的Handler,handleMessage(Message msg) 回调的线程时UI线程,这样就保证了从子线程切换到主线的逻辑,从这里可以看出,AsyncTask 也是借助 Handler 来实现线程切换的。继续往下看, AsyncTask 的构造方法,构造中初始化了两个对象,正是上一条介绍的 FutureTask 和 Callable,看过上一章的小伙伴们,看到这里,心里基本就有谱了, AsyncTask 原来是这么一回事,线程池+FutureTask+Callable+Handler。
我们一般是创建了 AsyncTask 对象后,执行耗时操作,是调用 execute() 方法,该方法形参是一个可变参数,看看调用的方法, executeOnExecutor(sDefaultExecutor, params);
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();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
方法里的线程池,就是静态模版块创建的线程池,所有AsyncTask共用一个,这个线程池可以自定义,然后通过反射调用 setDefaultExecutor(Executor exec) 方法,来进行替换。继续往下看,首先是Status状态的校验,Status mStatus = Status.PENDING; 表明初始状态是 PENDING,此时如果是RUNNING或者FINISHED,抛出异常,意思就是一个AsyncTask对象执行execute()方法只能执行一次。 执行该方法,mStatus = Status.RUNNING; 状态值开始改变,然后是 onPreExecute(); 方法,这个方法是暴露给外面的,可以重写,一般是弹出个Dialog框或者什么也不做;我们传进来的参数,此时赋值给 mWorker 对象的属性,mWorker是一个Callable;下一步,重点来了,用Executor执行了 mFuture 对象。 sDefaultExecutor 这个成员变量的来源是 SERIAL_EXECUTOR,而 SERIAL_EXECUTOR 是一个 SerialExecutor,看看它的代码
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);
}
}
}
会执行execute()方法,把 mFuture 传进来 ,此时,
new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
}
里面的 r 就是 mFuture,此时又创建一个 Runnable 把它包裹一下,然后添加到mTasks中,mTasks是一个ArrayDeque队列集合,此时 mActive 是个空对象,因此执行scheduleNext(); 方法,此时把 mTasks 队列中第一个对象赋值给 mActive,然后用默认的线程池来执行它。此时,就上面的那个Runnable,我们发现,它会执行包裹的mFuture,mFuture执行完后,会继续执行scheduleNext();方法,此时会继续从mTasks中头部取元素,赋值给mActive,然后继续用线程池执行它,直到mActive为null时停止。我们接下来分析mFuture 的 run()方法,重新回到 AsyncTask 构造方法中,我们发现,mFuture = new FutureTask<Result>(mWorker) ,结合上一条目,可以看出,会执行 WorkerRunnable 的 call() 方法,此时耗时操作 Result result = doInBackground(mParams); mParams 是我们传入的参数, result是耗时操作后得到的数据,doInBackground()这个方法就是需要我们重写的方法,此时它是处在子线程中,mTaskInvoked 设置为 true,表示做了doInBackground()操作。获取到 result 值后,调用 postResult(result) 方法,此时,创建一个 AsyncTaskResult 对象,把当前 AsyncTask 和 result 传入其中,然后把该对象传给Handler,通过what值为 MESSAGE_POST_RESULT 来做下一步操作,result.mTask.finish(result.mData[0]); AsyncTaskResult<?> result就是AsyncTaskResult对象,result.mTask 就是 AsyncTask, result.mData[0] 就是耗时操作的 Result result值,看看 finish()方法,此时,如果之前调用过 cancel()方法, 此时会执行 onCancelled(result); 方法,如果没有取消,则执行onPostExecute(result);方法,这两个方法都可以重写。然后就是 mStatus = Status.FINISHED; 改变 status 的状态值。上一条目中,介绍 FutureTask 时,讲过最后会执行 finishCompletion() 方法,该方法会执行 done(); 方法,也就是 mFuture 中的 done() 方法,即postResultIfNotInvoked(get()),get() 返回的耗时操作后获取的值 result,然后执行postResultIfNotInvoked()方法,
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
由于mTaskInvoked之前设置过true,此时是不会执行的,这个地方是为了以防万一的。
我们知道,在使用AsyncTask时,比如下载文件,可以显示进度,这是通过 publishProgress(Progress... values)方法设置数字,然后通过Handler,what码为MESSAGE_POST_PROGRESS,执行result.mTask.onProgressUpdate(result.mData); 代码,执行的是 AsyncTask 的 onProgressUpdate(Progress... values) 方法,我们可以重写此方法,获取values可变参数中对应的值即可。