字节跳动面试官:Android-中为什么需要-Handler-,一篇文章帮你解答

framew字节跳动面试官:Android-中为什么需要-Handler-,一篇文章帮你解答
orks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {

// 创建 Looper、Handler、MessageQueue
Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);

if (sMainThreadHandler == null) {
// 指定主线程的 handler 为 H
sMainThreadHandler = thread.getHandler();
}

// 开始准备接收消息
Looper.loop();
}
}

// 准备主线程的 Looper
frameworks/base/core/java/android/os/Looper.java
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException(“The main Looper has already been prepared.”);
}
sMainLooper = myLooper();
}
}

// prepare 方法中会创建一个 Looper 对象
frameworks/base/core/java/android/os/Looper.java
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException(“Only one Looper may be created per thread”);
}
sThreadLocal.set(new Looper(quitAllowed));
}

// Looper 对象创建的时候,同时创建一个 MessageQueue
frameworks/base/core/java/android/os/Looper.java
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

通过上面的流程,主线程的 Looper、MessageQueue 都已经创建好了,这就打下了 Message 机制的基础,后续有 Message 进来,就会进入主线程的 MessageQueue ,然后 Looper 会把他派发给对应的 Handler

对于主线程来说,这个 Handler 就在 ActivityThread 里面,下面截取一小段 Handler 的方法,大家可以看一下,熟悉代码的应该知道,BIND_APPLICATION 就是大家经常在 Systrace 中看到的新进程启动时候的那一段

frameworks/base/core/java/android/app/ActivityThread.java
class H extends Handler {
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int RECEIVER = 113;
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115;
public static final int STOP_SERVICE = 116;

public void handleMessage(Message msg) {
switch (msg.what) {
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “bindApplication”);
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
}
}

所以之前也有人会问,ActivityThread 是主线程么?为什么没有继承 Thread ?看了上面的流程,答案就很清楚了,ActivityThread 并不是一个 Thread ,只不过他有个叫 H 的 Handler 来处理主线程的 Message 而已;另外 Activity、Service、BroadcastReceiver 组件的生命周期都是在主线程执行的,通过上面的讲解应该就比较好理解了:都是在 ActivityThread 的 H 这个 Handler 里面处理的.

线程间通信

App 主线程初始化完成后,Message 机制跑起来了,那么子线程呢?其实也是一样的,子线程也有一套 Message 机制,有它自己的 MessageQueue、Looper、Handler。

那么线程间的 Message 通信就好理解了,Looper 在派发 Message 的时候,会根据 Message 自己的意愿(target,即目标 Handler),派发给对应的 Handler 去处理,这里的 Handler 既可以是子线程的 Handler ,也是主线程的 Handler,也可以是 App 进程里面其他的 Handler,只要知道且指定这个 Handler 即可

比如非常典型的一个例子就是 AsyncTask,如果你在主线程创建了 AsyncTask ,那么在 AsyncTask 执行在子线程完耗时任务后,就会给主线程的 Handler 发 Message,来更新主线程的 UI

子线程能不能更新 UI

既然主线程和子线程都是 Message 机制,子线程到底能不能更新 UI 呢?答案是可以的,但是会出问题,比如主线程正在执行 doFrame ,Measure 方法刚刚走完,你子线程把 View 的宽高给改掉了,那么主线程后续执行 Layout 的时候,View 的位置摆放就可能会出问题(当然我没有试过,感兴趣的小伙伴可以把 checkThread 限制代码去掉,然后在子线程更新 UI 试试)

所以 Android 为了避免这种情况,在有 UI 更新的地方,都加了 checkThread() ,来确保只有主线程才能更新 UI

void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
ckThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(

上一篇:一次Kafka内存泄露排查经过


下一篇:Pygame实战:风靡全球的经典泡泡龙小游戏来袭,你会喜欢嘛?(附源码)