为什么必须在主线程中加载 AsyncTask?
首先这句话不是绝对的,我们看一下不同版本 AsyncTask 的源码就知道为什么了。
先上结论:API 16 以前必须在主线程加载 AsyncTask,API 16 以后就不用了。
首先,所有版本的 AsyncTask 都是在成员位置直接声明并创建了 Handler 对象,它是一个静态变量,也就是说它是在类加载的时候创建的:1
private static final InternalHandler sHandler = new InternalHandler();
API 16 以前的 AsyncTask
在 Android 4.1(API 16)之前,如果是在子线程加载的 AsyncTask,那么就会导致静态的 Handler 对象创建并初始化(如上),而 Handler 默认采用的是当前线程的 Looper,于是 Handler 用了子线程的 Looper,处理消息时也就无法切换到主线程,而是被 Handler post 到创建它的那个线程执行(即子线程)。
所以那个年代,我们不能在子线程加载 AsyncTask。
API 16 以后,API 22 之前的 AsyncTask
在 API 16 以后,API 22 之前,Google 进行了改动:在 ActivityThread 的 main 函数里面(App启动的入口),直接调用了 AsyncTask 的 init() 方法,该方法代码如下1
2
3public static void init() {
sHandler.getLooper();
}
main 函数运行在主线程,这样就确保了 AsyncTask 类是在主线程加载。因此 AsyncTask 的 Handler 用的也是主线程的 Looper。
所以在 API 16 以后,我们是可以在子线程中加载 AsyncTask的。但是不管我们需不需要 AsyncTask,它都会在我们 App 启动时就加载,那如果我们完全没有用到它,岂不是很浪费资源?
API 22 之后
在 Android 5.1 (API 22) 之后 Google 又换了一种实现:
删除了 AsyncTask 的 init 方法,main 函数中也不再调用它。
在 AsyncTask 成员变量位置仅仅声明了静态的 Handler,并没有创建对象:1
private static InternalHandler sHandler; // 还去掉了final关键字
在 Handler 的实现中,添加了一个无参构造:1
2
3public InternalHandler() {
super(Looper.getMainLooper());
}
在 Handler 的实现中,添加了 getHandler() 方法:在需要 Handler 时会调用此方法。1
2
3
4
5
6
7
8private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
可以看到,这样既保证了 Handler 采用主线程的 Looper 构建,又使得 AsyncTask 在需要时才被加载。
以上,Google 在 API 16 以后通过不同的实现,确保了线程的正常切换。注意一点,onPreExecute()不受 Handler 影响,运行在执行 execute 方法的线程,原因我们后面分析。
Android4.1之前AsyncTask类必须在主线程中加载,但是在之后的版本中就被系统自动完成。而在Android5.0的版本中会在ActivityThread的main方法中执行AsyncTask的init方法,而在Android6.0中又将init方法删除。所以在使用这个AsyncTask的时候若是适配更多的系统的版本的话,使用的时候就要注意了。
Android AsyncTask 解析
Android UI 主线程,啥玩意?还有Handler+Looper+MessageQueue几个意思?