Android进阶之路Android开发经验谈Android技术知识

聊一聊Android的Handler

2019-07-24  本文已影响4人  2c3d4f7ba0d4

Handler对于Android开发者而言,应该是再熟悉不过的了。每个Handler在创建时,就会绑定于一个特定线程内(创建时所在的线程),同时也绑定于该线程所在的Message Queue。

Handler是做什么的?

归类下来,其实是两个用处:

  1. 管理该线程内未来任务的执行次序。(schedule功能)
  2. 不同线程之间的消息通信。(包括UI线程和非UI线程之间的通信,或者两个非UI线程之间的通信)

第一个作用,我们一般是通过 post 系列方法(post, postDelayed, postAtTime)或者 sendMessage 系列方法(sendMessage, sendMessageAtTime, sendMessageDelayed, sendEmptyMessage)实现。 post系列方法是传递的一个Message,send系列方法是传递的一个Message。

Handler和Looper到底是什么关系?

Handler的 schedule 任务功能是通过 Looper 和 MessageQueue 实现的。Looper 可以开启一个消息循环,不停的处理进入到该循环内的消息。这些消息是通过一个消息队列(MessageQueue)来存储管理的。

UI线程默认就开启了消息循环(系统的 MainLooper),其他线程默认没有开启,可以通过Looper的 prepare() 和 loop() 方法进行开启。

因此,Handler 和 Looper 的关系其实和 Thread 分不开的。每一个 Handler 在创建时会依附于它所在的thread,而每一个thread 也都有自己的一个 looper。对于用户(开发者)而言,looper 的消息循环功能主要是通过 handler 来进行调用的。

每个线程都有自己的一个Looper吗?

是的,每个线程都有自己的一个 looper,主线程( UI 线程)的 looper 是 MainLooper。

为什么主线程的Looper循环不会造成ANR?

看过一些 Android 源码的同学应该知道,有一个 ActivityThread 类,其中有个 main() 方法,这个是整个app程序的入口。而在 main() 方法里,会开启一个 Looper 的 loop()方法(也即是我们平时说的主线程的 MainLooper)。

前面我们有提到,loop() 其实就是一个循环,不停的去查询消息。可能有同学会有疑问,既然它是主线程里的一个死循环,那为什么它不会造成 ANR 呢?

回答这个问题,我们首先要理解 ANR 的本质:ANR 是主线程里的操作太耗时时(一般是超过5s),Android 弹出的一个提示。而我们这里说的 loop 循环,是不会引起这种情况的。looper 使用了 linux 上的 epoll 机制,在没有新消息过来时,线程会被挂起,让出 CPU 资源而进入休眠状态。其实主线程大多数时候是处于休眠状态的,并不会大量占用CPU资源。

简单讲,其他线程可能会源源不断的向主线程发送消息,这些消息都会在主线程的 MessageQueue 里。主线程的 looper 会去轮询,从 MessageQueue 里取出消息,分发给主线程的 handler 去执行(参考 ActivityThread 的内部类 H)。在队列里没有消息时,主线程会被挂起,让出 CPU 资源(这是 linux 的 epoll 机制)。在有新的消息过来时,主线程又会被唤醒继续起来工作。

也即是,ANR 是主线程里的某些操作太耗时,导致不能响应用户的交互。而 looper 的循环是在有需要时去取出消息供执行,不需要时即休眠,loop 的循环操作本身是不会影响 ANR 的。

为什么 Handler 使用不当可能会造成内存泄露?

Handler 造成内存泄露最常见的场景是,在 activity 里直接初始化了一个非静态类的 Handler 实例,这种情况下,当 activity 需要销毁时,如果主线程的消息队列中还存在没有处理完的 Message,就会造成内存泄露。

主要的原因其实有下面几点:

综上,在 activity 实例待销毁时,如果这个 activity 里存在持有 activity 引用的 Handler,并且消息队列里还有消息未处理完,就可能造成 activity 不能被回收。

规避方式:

最稳妥的方式是不要使用非静态内部类,并且如果需要持有 activity 的引用的话,要通过弱应用的方式。

上一篇 下一篇

猜你喜欢

热点阅读