AsyncTask 的源码我们要是只看原理不难的,我就不跟着大众的思路去看了,先来看看 AsyncTask 类中都有哪些关键变量,这里只放重要的。
我研究的代码是 API 26 的
AsyncTask 声明的核心变量
public abstract class AsyncTask<Params, Progress, Result> {
private static final String LOG_TAG = "AsyncTask";
// 线程池设置参数
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
// 线程池工厂对象
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
// 线程池阻塞队列
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
// AsyncTask 里面的2个核心线程池对象
public static final Executor THREAD_POOL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
// 2个用于切换线程,发送数据的 handle
private static InternalHandler sHandler;
private final Handler mHandler;
// 静态代码段
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;
}
再次说一下啊,不难看的,我一定让大家都看得懂,大家都懂了,我也就懂啦
AsyncTask 上来声明的全局变量不少,但是类型不多,看着我上面的注解都能明白,期中:
- 先是一段线程池的参数设置,比如 CORE_POOL_SIZE 核心线程数,MAXIMUM_POOL_SIZE 最大线程数,KEEP_ALIVE_SECONDS 空闲线程存活时间,ThreadFactory 线程创建工厂,BlockingQueue 线程池任务队列,这几个不了解的去看看 java 的多线程,随便找个资料有个半小时足够了解了
- 然后是 2个线程池对象 sDefaultExecutor 和 THREAD_POOL_EXECUTOR,至于为啥有2个线程池,现在只能说是历史原因,AsyncTask 历次改版多次,得考虑版本兼容把,所以不能删,后面会说清的
- 再后面是2个 handle 对象,大家也会问为啥有2个了把,这个也是后面说
- 静态代码块中,初始化了 THREAD_POOL_EXECUTOR 这个线程池
AsyncTask 的声明我们看完了,再来看看构造方法干啥了
public AsyncTask() {
this((Looper) null);
}
public AsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
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 不用管,是包装 runnbale 任务的,不影响我们理解执行过程
- 有3个构造函数,我们平时 new AsyncTask 对象时大家传参数了嘛,都没有吧,所以大家说会执行哪个构造方法呢?
- 那肯定是这个 AsyncTask(@Nullable Looper callbackLooper) 啦,而且参数传的是 null 值,然后里面会发生什么事呢
- mHandler 就会= getMainHandler() 返回的参数了
getMainHandler 方法
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
InternalHandler 类
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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;
}
}
}
这 2 段代码很明确的告诉我们 mHandler = getMainHandler() 干啥了:
- 会用 UI 线程的 looper 把 sHandler 这个对象 new 出来
- 然后把 sHandler 的值赋 给 mHandler
- 很显然这么写的话 mHandler 就是妥妥的打酱油啦,sHandler 才是主角啊,这点很重要啊,套路在此啊,膜拜套路大神啊........
好了我们知道了 AsyncTask 都有啥我们很重要的部件了,然后我们看看 AsyncTask 是怎么运行的,这当前得出入口的那个执行方法来啦
execute 方法
public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}
execute 方法我们可以看到是使用了 sDefaultExecutor 这个线程池
executeOnExecutor 方法
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;
}
- executeOnExecutor 方法先判断了 AsyncTask 对象的状态,不是 running 运行中的话抛出异常中断执行
- 然后刷新了 AsyncTask 对象的当前状态
- 然后执行了我们 重写的 onPreExecute 方法
- 把参数设置进去runnable 任务中 ,这个 mWorker 就是 AsyncTask 里面封装的 runnable 任务类型对象,有兴趣的自己去看,我这里不涉及到这个内容。
- 最后执行用线程池执行包装了好几层的异步任务
重点来了,我们来看看 sDefaultExecutor 这个线程池对象,sDefaultExecutor 的类型是 AsyncTask 的内部类 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);
}
}
}
- SerialExecutor 直接实现了 Executor 线程池这个根接口
- execute 运行方法首先往任务列表里添加了一个任务进去,Runnable 对象
- 然后判断当前是不是有任务正在执行,没有的话就拿到一个任务去线程池执行
- 大家注意这时用的是 THREAD_POOL_EXECUTOR 这个线程池
我们来看看往任务队列中添加任务时的 Runnable 对象。run 方法里首先执行的是后台任务,然后就是在任务完成后拿到下一个任务去 线程池里去执行,循环就是这么跑起来的。
总结下核心思路
源码看到上面就算完事了,线程池的执行思路已经很清楚了,总结下:
- AsyncTask 的2个线程池,SerialExecutor 类型的 sDefaultExecutor 对象负责存储,分发任务;ThreadPoolExecutor 类型的 THREAD_POOL_EXECUTOR 对象负责执行任务
- sDefaultExecutor 线程池会给任务对象加点料,既在任务结束时添加获取下一个任务去执行的代码,然后把这个加了料的任务抛给 THREAD_POOL_EXECUTOR 线程池对象去执行,这样一个串行循环就跑起来了,一个执行完了再去取下一个任务执行。
- ThreadPoolExecutor 类型的这线程池空有多个核心线程,其实每次都是在单线程在跑,要不怎么串行的起来,浪费了,为啥会这样恩,历史原因呗
AsyncTask 的历史
这里借鉴《开发艺术探索》的内容:
- 1.6 之前 AsyncTask 是串型执行的
- 1.6 时改成并行执行的
- 3.0 时改回串行执行了,因为并发执行在 刷新 UI 时可能会有问题。
但是 AsyncTask 还是有并行执行的方法
DownloadTask downloadTask = new DownloadTask();
downloadTask2.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, urls);
3.0 开始提供了 executeOnExecutor 方法,重点在于替换 AsyncTask 里面的默认线程池对象,使用 AsyncTask 的常量 AsyncTask.THREAD_POOL_EXECUTOR 这个线程池。
AsyncTask 如何在 UI 线程刷新数据
这点大家肯定会想到 handle 了吧,在 AsyncTask 构造方法里初始化了 mWorker 对象,这个就是 Runnable 的包装类型,里面在执行完后异步任务后拿到数据时用到了 postResult(result) 这个方法
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
postResult(result) 方法
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
postResult 方法里面用 handle 对象生成了一个 message 对象,然后发射出去,重点在于用的哪个 handle 对象,大家还记得上面我说的谁是主角的套路了吧,
private Handler getHandler() {
return mHandler;
}
那当然就是 mHandler 啦,要不然给 mHandler 做这么多戏干嘛,然后我们顺着 mHandler 的InternalHandler 类型可以获得最终想要的地方
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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 void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
没啥说的了吧,其实我们仔细看的话 很多源码都能看的懂的,即便是又不懂的地方先放下,其实核心的逻辑思路大半都能看懂,其实这些的话就能应付面试啦.
2019.4.22 补充
AsyncTask 开启线程的方法 asyncTask.execute() 默认是也是开启一个线程和一个队列的,不过也可以通过asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 0)开启一个含有5个新线程的线程池,也就是说有个5个队列了,假如说你执行第6个耗时任务时,除非前面5个都还没执行完,否则任务是不会阻塞的,这样就可以大大减少耗时任务延迟的可能性,这也是它的优点所在。当你想多个耗时任务并发的执行,那你更应该选择AsyncTask。