AsyncTask 是 Android 为我们提供执行异步任务的一个轻量的类, 可以用来处理耗时操作, 并且能够很方便的将执行结果返回给主线程. 本篇文章将会通过源码分析来介绍 AsyncTask 的内部实现原理.
目录
重要的成员变量
AsyncTask 构造分析
两个线程池
图解 AsyncTask 执行过程
执行结果是如何被传递到主线程
onProgressUpdate() 是什么时候调用
1. 重要的成员变量
AsyncTask 里面几个重要的成员变量变量分别为:
名称 | 作用 | 创建 | 调用 | 备注 |
---|---|---|---|---|
THREAD_POOL_EXECUTOR | 真正执行任务的线程池 | 在静态代码块中被创建 | 在 SerialExecutor 线程池的 scheduleNext 方法中被调用 | 该线程成池的核心线程数量是根据手机 cup 核数 - 1 确定的 |
sDefaultExecutor | 内部创建队列用于储存异步任务 | 创建类的成员变量的时候被创建 | 在 AsyncTask 的 execute() 中被作为参数传递 | SerialExecutor 类的 scheduleNext 方法中会将任务添加到 THREAD_POOL_EXECUTOR 线程池中执行 |
mWorker | 任务最终执行方法,其内部的 call 方法会调用 doInBackground() 方法 | 在 AsyncTask 有参构造中创建 | WorkerRunnable 在 FutureTask 的 run 方法中被调用该类的 call 方法 | 其继承自 Callable 方法,一般配合 FutureTask 使用 |
mFuture | 在其内部会调用 mWorker 的 call 方法来执行任务 | 在 AsyncTask 有参构造中创建 | FutureTask 在 SerialExecutor 类的 execute 方法中被调用 | 该成员变量被 AsyncTask 的 executeOnExecutor() 中传递到 SerialExecutor 中 |
sHandler | 用于将在结果返回到主线程 | 在 AsyncTask 有参构造中通过调用 getMainHandler 来创建 | 在 postResult() 中通过复用 Message 来调用 | InternalHandler 类的 Looper 是主线程的 Looper |
2. AsyncTask 构造分析
在分析 AsyncTask 之前我们先看看他的构造, 我们在使用 AsyncTask 经常使用空参构造的方式来创建该对象, 这个构造方法内部会调用他的有参构造. 首先有参会先根据是否有 Looper 来创建 Handler. 如果传入的 Looper 为空或者传入的 Looper 不是主线程的 Looper, 则调用 getMainHandler() 来创建 Handler; 如果是主线程的 Looper 则以此 Looper 重新 new 一个 Handler. 当 Handler 创建完毕后然后在以次创建 WorkerRunnable 和 FutureTask. 下面为 AsyncTask 构造源码:
- public AsyncTask(@Nullable Looper callbackLooper) {
- // 创建 Hanlder
- 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);
- // 获取异步执行结果
- result = doInBackground(mParams);
- // 将进程中未执行的命令一并送往 cup 处理
- Binder.flushPendingCommands();
- } catch (Throwable tr) {
- mCancelled.set(true);
- throw tr;
- } finally {
- // 将处理结果返回到主线程
- postResult(result);
- }
- return result;
- }
- };
- //FutureTask 间接调用了 WorkerRunnable 方法的 call 方法
- // 来获取执行结果
- 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);
- }
- }
- };
- }
从源码中我们可以知道, mHandler 实际上是 InternalHandler,mWorker 内部的 call() 方法会调用 doInBackground,try 块不管执行结果如何, 都会调用 postResult() 来调用 Hanlder 发送消息, 通知主线程最 Ui 更新操作. 先有一个问题, call() 方法是在哪里会被调用呢? 其实是在 mFuture 内部的 run() 方法中调用 mWorker 他的 call 方法. 具体代码读者可以自行查找项目源码, 这里就不多说了. 上面提到的 mWorker,mFuture 会在 execute() 方法中被调用和传递, execute() 是用于配置和启动任务的方法, 下面为该方法的部分代码.
- /**
- * 在主线程中执行
- * 可传入一个或多个参数
- */
- @MainThread
- public final AsyncTask.<Params, Progress, Result> execute(Params... params) {
- return executeOnExecutor(sDefaultExecutor, params);
- }
3. 两个线程池
executeOnExecutor(sDefaultExecutor, params); 方法将参数 params 和 sDefaultExecutor 传入该方法中, 并返回一个 AsyncTask. 这个 params 我们知道它是我们传进来的参数, 但是 sDefaultExecutor 是什么呢? 它是一个线程池, 是一个类的成员变量.
- public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
- private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
既然我们知道 sDefaultExecutor 是一个线程池, 也就是 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() {
- //mTask.pll() 删除队列中的第一个元素, 并返回该元素的值
- if ((mActive = mTasks.poll()) != null) {
- // 调用线程池执行异步
- THREAD_POOL_EXECUTOR.execute(mActive);
- }
- }
- }
从上面的代码我们可以知道, SerialExecutor 类中创建一个双端队列 ArrayDeque, 用于储存异步任务. 他还有 execute() 和 scheduleNext() 方法, execute() 内部调用了 mTasks.offer 用于将传入的异步任务添加到队列中, 然后在调用 scheduleNext() 方法. scheduleNext() 方法调用 mTask.poll() 方法取出并删除第一个元素, 最后将取出的元素放到线程池中. 不知道读者有没有发现 AsyncTask 内部其实是有两个线程池 SerialExecutor 和 THREAD_POOL_EXECUTOR, 其中 SerialExecutor 线程池主要是用于将任务添加到队列中, 而任务真正的执行是在 THREAD_POOL_EXECUTOR 线程池中.
4. 图解 AsyncTask 执行过程
要想知道执行结果是如何被传递到线程中, 我们先搞明白 AsyncTask 的执行过程. 其实读者从上面的内容中或许能改猜到它的大概执行过程. 其实它的执行过程也不复杂我们可以结果下面这张图进行分析:
我们在使用 AsyncTask 的时候会先创建对象, 然后调用 execute() 方法传入参数执行任务:
- // 创建 AcyncTask 封装类
- TestAsyncTask asyncTask = new TestAsyncTask();
- // 传入参数, 执行任务
- asyncTask.execute(5,6,7);
我们在通过上面操作执行任务的时候, 其实 AsyncTask 内部做了一下几个操作:
在构造中创建 Handler,WorkerRunnable,FutureTask
在
executeOnExecutor()
中校验该任务是否在任务栈中执行, 或者是否已完成过
如果该未任务在执行, 或者未完成过. 将会包装传入的参数然后再将 FutureTask 添加到线程池中调用 execute() 方法执行异步
SerialExecutor 线程池的 execute() 方法创建 Runnable, 并添加到队列中.
scheduleNext() 方法取出队列中的第一个 Runnable, 加他添加到 THREAD_POOL_EXECUTOR 线程池中开始执行任务
Runnable 调用 FutureTask 的 run() 方法执行 WorkerRunnable 的 call() 方法
WorkerRunnable 的 call() 方法执行完, SerialExecutor 线程池的 execute() 方法再次调用 scheduleNext() 执行下个任务.
结合上面的执行流程图我们知道, 在经过上面 7 个步骤异步任务一个一个的在线程池中被完成. 既然我们知道了 AsyncTask 的大致执行过程, 那么它是如何将执行结果返回到主线程呢? 下面我们将会来分析.
5. 执行结果是如何被传递到主线程
我们知道 doInBackground() 函数是我们的任务具体执行函数. 这个函数是在 WorkerRunnable 的 call() 函数中被调用, 从上面的执行过程介绍中我们知道 call() 方法是在 FutureTask 的 run 方法执行的时候被调用的. 当 call() 方法在执行完 doInBackground() 方法得到结果后, 会将该结果传递给 postResult() 方法:
- private Result postResult(Result result) {
- //obtainMessage 方法是从 Message 池中获取一个 Message 对象, 避免重复创建.
- Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
- new AsyncTaskResult<Result>(this, result));
- // 发送消息
- message.sendToTarget();
- return result;
- }
postResult() 方法内代码也很简单, 首先它会通过 Hanlder(注: 从文章开始部分我们可以知道, 这个 Handler 的 Looper 是主线程的 Looper) 在消息队列中获取一个 Message 对象, 然后将结果和定义的标记包装到 Massage 中, 最后在通过 Message 对象调用 sendToTarget() 将消息发出. 既然消息发送出去了, 那么消息是在哪里执行呢? 答案是: 在 InternalHandler 类中的 handleMessage() 中被执行. why? 因为 getHandler() 获取的是 Hanlder 是我们在文章开始介绍的构造函数中被 getMainHandler() 赋值的 mHandler, 而 getMainHandler() 中返回的就是 InternalHandler. 既然我们知道了消息在哪里被处理, 那么我们可以看一看它的具体处理逻辑:
- 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;
- }
- }
handleMessage() 内部有两个判断, 如果标识是 MESSAGE_POST_RESULT 则将结果传递给 finish() 方法. 如果标识是 MESSAGE_POST_PROGRESS 则调用 onProgressUpdate() 用于更新进度. 下面我们先看 finish() 方法的源码:
- private void finish(Result result) {
- if (isCancelled()) {
- onCancelled(result);
- } else {
- onPostExecute(result);
- }
- mStatus = AsyncTask.Status.FINISHED;
- }
finish() 方法会判断是否取消了该任务, 如果用户调用了 cancel() 函数那么 isCancelled() 返回的就是 true, 当用户取消了任务那么将会回调 onCancelled(result) 函数而 onPostExecute() 则不会调用, 反之则会调用.
6. onProgressUpdate() 是什么时候调用
在分析 handleMessage() 方法的时候我们留了一个小尾巴, MESSAGE_POST_PROGRESS 这个标记消息在什么时候发出的? 在回答这个问题之前, 我们先回忆一下我们在使用 doInBackground() 的时候, 是否有在其内部调用 publishProgress() 函数来更新进入? 回忆到这里答案就很明显了: 通过 Handler 发生更新进度消息的操作是在 publishProgress() 函数中完成的. 下面为该函数的源码:
- @WorkerThread
- protected final void publishProgress(Progress... values) {
- // 如果任务没有取消, 则发生消息更新进度
- if (!isCancelled()) {
- getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
- new AsyncTaskResult<Progress>(this, values)).sendToTarget();
- }
- }
从上面的源码我们可以知道, 更新进度的消息是在子线程中发送的, 如果该任务没有被取消那么就可以发现消息. 这种通过复用 Message 对象发送信息的方式对性能上有起到优化的作用. 读者可以在文章结尾的参考链接中找到相关的介绍, 笔者就不介绍了.
总结
文章到这里对与 AsyncTask 的源码分析也就介绍完了. 在 AsyncTask 中比较重要的成员变量为: WorkerRunnable,FutureTask 已经两个线程池, 能够真正理解 AsyncTask 的执行过程一定要搞明白他们几个的调用过程. 最后感谢您能在百忙之中抽出时间阅读这篇文章, 下一篇文章将会写一下 HandlerThead 和 IntentService.
参考
ArrayDeque 类的使用详解
Android 线程优先级
剖析 Android 中进程与线程调度之 nice
带你轻松看源码 ---AsyncTask(异步任务)
Android Message 和 obtainMessage 的区别 https://www.cnblogs.com/xuan52rock/p/4932617.html
来源: https://juejin.im/post/5c7dece3e51d4558d360680b