2024年3月27日发(作者:)

前言

Android

的消息机制主要是指

Handler

的运行机制,对于大家来说

Handler

已经是轻车熟路了,可是真的掌握了

Handler

?本文主要通过几个问题围绕着

Handler

展开深入并拓展的了解。

站在巨人的肩膀上会看的更远。大家有兴趣的也可以到

Gityuan

的博客上多了解了解,全部都是干货。而且他写

的东西比较权威,毕竟也是小米系统工程师的骨干成员。

Questions

1.

Looper

死循环为什么不会导致应用卡死,会消耗大量资源吗?

2. 主线程的消息循环机制是什么(死循环如何处理其它事务)?

3.

ActivityThread

的动力是什么?(

ActivityThread

执行

Looper

的线程是什么)

4.

Handler

是如何能够线程切换,发送

Message

的?(线程间通讯)

5. 子线程有哪些更新

UI

的方法。

6. 子线程中

Toast

showDialog

,的方法。(和子线程不能更新

UI

有关吗)

7. 如何处理

Handler

使用不当导致的内存泄露?

8.

Looper

死循环为什么不会导致应用卡死?

线程默认没有

Looper

的,如果需要使用

Handler

就必须为线程创建

Looper

。我们经常提到的主线程,也叫

UI

线程,

它就是

ActivityThread

ActivityThread

被创建时就会初始化

Looper

,这也是在主线程中默认可以使用

Handler

的原

因。

首先我们看一段代码

new Thread(new Runnable() {

@Override

public void run() {

Log.e("qdx", "step 0 ");

e();

xt(, "run on Thread", _SHORT).show();

Log.e("qdx", "step 1 ");

();

Log.e("qdx", "step 2 ");

}

}).start();

我们知道

();

里面维护了一个死循环方法,所以按照理论,上述代码执行的应该是

step 0 –>step 1

就是说循环在

e();

();

之间。

在子线程中,如果手动为其创建了

Looper

,那么在所有的事情完成以后应该调用

quit

方法来终止消息循环,否则这个子

线程就会一直处于等待(阻塞)状态,而如果退出

Looper

以后,这个线程就会立刻(执行所有方法并)终止,因此建议

不需要的时候终止

Looper

执行结果也正如我们所说,这时候如果了解了

ActivityThread

,并且在

main

方法中我们会看到主线程也是通过

Looper

方式来维持一个消息循环。

public static void main(String[] args) {

eMainLooper();//

创建

Looper

MessageQueue

对象,用于处理主线程的消息

ActivityThread thread = new ActivityThread();

(false);//

建立

Binder

通道

(

创建新线程

)

if (sMainThreadHandler == null) {

sMainThreadHandler = dler();

}

nd(_TAG_ACTIVITY_MANAGER);

();

//

如果能执行下面方法,说明应用崩溃或者是退出了

...

throw new RuntimeException("Main thread loop unexpectedly exited");

}

那么回到我们的问题上,这个死循环会不会导致应用卡死,即使不会的话,它会慢慢的消耗越来越多的资源

吗?

对于线程即是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我

们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下

去的,死循环便能保证不会被退出,例如,

binder

线程也是采用死循环的方法,通过循环方式不同与

Binder

驱动进行读

写操作,当然并非简单地死循环,无消息时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去处理其他

事务呢?通过创建新线程的方式。真正会卡死主线程的操作是在回调方法

onCreate/onStart/onResume

等操作时间过长,

会导致掉帧,甚至发生

ANR

本身不会导致应用卡死。

主线程的死循环一直运行是不是特别消耗

CPU

资源呢?

其实不然,这里就涉及到

Linux pipe/epoll

机制,简单说就是在主

线程的

MessageQueue

没有消息时,便阻塞在

loop

()

中的

nativePollOnce()

方法里,此时主线程会释放

CPU

源进入休眠状态,直到下个消息到达或者有事务发生,通过往

pipe

管道写端写入数据来唤醒主线程工作。这里采用的

epoll

机制,是一种

IO

多路复用机制,可以同时监控多个描述符,当某个描述符就绪

(

读或写就绪

)

,则立刻通知相应程序

进行读或写操作,本质同步

I/O

,即读写是阻塞的。

所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量

CPU

资源。

Gityuan–Handler(Native

)

2.

主线程的消息循环机制是什么?

事实上,会在进入死循环之前便创建了新

binder

线程,在代码

()

中: