AsyncTask的工作原理
为了分析 AsyncTask 的工作原理,从它的 execute 方法开始分析,execute 方法又会调用 executeOnExecutor 方法,它们的实现如下所示:
1 | public final AsyncTask<Params, Progress, Result> execute(Params... params) { |
在上面的代码中,sDefaultExecutor 是一个串行的 Executor(注意它不是线程池,只是实现了 Executor 接口),一个进程中所有的 AsyncTask 全部在这个串行的 Executor 中排队执行。在 executeOnExecutor 方法中,AsyncTask 的 onPreExecute 方法最先执行,然后 Executor 开始执行。
1 | public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); |
从 SerialExecutor 的实现可以分析 AsyncTask 的排队执行过程。首先系统会把 AsyncTask 的 Params 参数封装为 FutureTask 对象,FutureTask 是一个并发类,在这里它充当了 Runnable 的作用。接着这个 FutureTask 会交给 SerialExecutor 的 execute 方法去处理,SerialExecutor 的 execute 方法首先会把 FutureTask 对象插入到任务列表 mTasks 中,如果这个时候没有正在活动的 AsyncTask 任务,那么就会调用 SerialExecutor 的 scheduleNext 方法来执行下一个 AsyncTask 任务。同时当一个 AsyncTask 任务执行完后,AsyncTask 会继续执行其它任务直到所有的任务都被执行为止,从这一点可以看出,在默认情况下,AsyncTask 是串行执行的。
AsyncTask 中有两个 Executor(SerialExecutor 和 THREAD_POOL_EXECUTOR)和一个 Handler(IntentHandler),其中 SerialExecutor 用于任务的排队,而线程池 THREAD_POOL_EXECUTOR 用于真正地执行任务,IntentHandler 用于将执行环境从线程池切换到主线程。在 AsyncTask 的构造方法中有如下一段代码,由于 FutureTask 的 run 方法会调用 mWorker 的 call 方法,因此 mWorker 的 call 方法最终会在线程池中执行。
1 | mWorker = new WorkerRunnable<Params, Result>() { |
在 mWorker 的 call 方法中,首先将 mTaskInvoked 设为 true,表示当前任务已经被调用过了,然后执行 AsyncTask 的 doInBackground 方法,接着将其值传递给 postResult 方法,它的实现如下所示:
1 | private Result postResult(Result result) { |
在上面的代码中,postResult 方法会通过 sHandler 发送一个 MESSAGE_POST_RESULT 的消息,这个 sHandler 的定义如下所示:
1 | private static InternalHandler sHandler = new InternalHandler(); |
可以发现,sHandler 是一个静态的 Handler 对象,为了能够将执行环境切换到主线程,这就要求 sHandler 这个对象必须在主线程中创建。由于静态成员会在加载类的时候进行初始化,因此这就变相要求 AsyncTask 的类必须在主线程中加载,否则同一个进程中的 AsyncTask都将无法正常工作。sHandler 收到 MESSAGE_POST_RESULT 这个消息后会调用 AsyncTask 的 finish方法,如下所示:
1 | private void finish(Result result) { |
AsyncTask 的 finish 方法的逻辑比较简单,如果 AsyncTask 被取消执行了,那么就调用 onCancelled 方法,否则就会调用 onPostExecute 方法,可以看到 doInBackground 的返回结果会传递给 onPostExecute 方法,到这里 AsyncTask 的整个工作过程就分析完毕了。
通过分析 AsyncTask 的源码,可以进一步确定,从 Android3.0 开始,默认情况下 AsyncTask 的确是串行执行的,在这里通过一系列实验来证实这个判断。
请看如下代码,代码很简单,就是单击按钮的时候同时执行5个 AsyncTask 任务,每个 AsyncTask 会休眠3s来模拟耗时操作,同时把每个 AsyncTask 执行结束的时间打印出来,这样就可以观察出 AsyncTask 到底是串行执行还是并行执行。
1 |
|
在 Android 6.0 的设备上运行程序,看下面的运行结果:
从下面 Log 可以看出,5个 AsyncTask 共耗时15s(AsyncTask#5的时间减去 AsyncTask#1 的时间再加上 AsyncTask#1 打印 Log 开始睡眠的3秒,即18-6+3=15)且时间间隔为3s,很显然是串行执行的。
1 | com.example.wy521angel.asynctasktest E/MainActivity: AsyncTask#1execute finish at2018-01-12 09:41:06 |
Android 3.0 以下的设备暂未测试。
为了让 AsyncTask 可以在 Android 3.0 及以上的版本上并行,可以采用 AsyncTask 的 executeOnExecutor 方法,需要注意的是这个方法是 Android3.0 新添加的方法,并不能在低版本上使用,如下所示:
1 | (Build.VERSION_CODES.HONEYCOMB) |
在 Android 6.0 的设备上运行程序,运行结果如下:
1 | com.example.wy521angel.asynctasktest E/MainActivity: AsyncTask#1execute finish at2018-01-12 10:12:59 |
很显然,我们的目的达到了,成功的让 AsyncTask 在6.0的手机上并行起来了。
代码见:AsyncTask串行并行测试
参考资料:
《Android 开发艺术探索》任玉刚 第11章 11.2 11.2.2 AsyncTask的工作原理