为什么必须在主线程中加载 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
3
public 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
3
public InternalHandler() {
super(Looper.getMainLooper());
}

在 Handler 的实现中,添加了 getHandler() 方法:在需要 Handler 时会调用此方法。

1
2
3
4
5
6
7
8
private 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几个意思?

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器