Handler

Looper :负责关联线程以及消息的分发在该线程下从 MessageQueue 获取 Message,分发给 Handler ;

MessageQueue :是个队列,负责消息的存储与管理,负责管理由 Handler 发送过来的 Message ;

Handler : 负责发送并处理消息,面向开发者,提供 API,并隐藏背后实现的细节。

Handler 发送的消息由 MessageQueue 存储管理并由 Loopler 负责回调消息到 handleMessage()。

线程的转换由 Looper 完成,handleMessage() 所在线程由 Looper.loop() 调用者所在线程决定。

什么是 Handler?

Handler 是 Android 的一种消息处理机制,与 Looper,MessageQueue 绑定,可以用来进行线程的切换。常用于接收子线程发送的数据并在主线程中更新 UI

Handler线程通信的原理(线程切换)

每个线程都有一个自己的消息队列,线程可以开启一个死循环不断地从队列中读取消息。

当 B 线程要和 A 线程通信时,只需要往 A 的消息队列中发送消息,A 的事件循环就会读取这一消息从而实现线程间通信

ThreadLocal

一个 ThreadLocal 实例在不同的线程中调用 get 方法可以取出不同的值。

ThreadLocal.set 可以将一个实例变成线程的成员变量

ThreadLocal.set

根据当前线程获取线程的一个 map 对象,然后把 value 放入 map 中,达到将 value 变成线程的成员变量的目的

ThreadLocal.get

set 方法差不多,区别就是一个将 value 写入 map,一个从 map 中读取 value

为什么要将 ThreadLocal 作为 Looper 的设置和获取工具呢?

确保线程间的绑定因为每一个ThreadLocal对于线程来说就是身份证

Looper 要放在线程中的,每个线程只需要一个事件循环,只需要一个 Looper。事件循环是个死循环,多余的事件循环毫无意义。ThreadLocal.set 可以将 Looper 设置为线程的成员变量

同时为了方便在不同线程中获取到 Looper,Android 提供了一个静态对象 Looper.sThreadLocal。这样在线程内部调用 sThreadLocal.get 就可以获取线程对应的 Looper 对象

消息队列中没有消息了,这个死循环会一直“空转”吗?

MessageQueue 中,有两个 native 方法,nativePollOncenativeWake

nativePollOnce 表示进行一次轮询,来查找是否有可以处理的消息,如果没有就阻塞线程,让出 CPU 资源

Looper 是个死循环,为什么不会导致 ANR 呢?

ANR 是应用在特定时间内无法响应一个事件时抛出的异常。

Looper 死循环是事件循环的基石,本身就是 Android 用来处理一个个事件的。正常情况下,触摸事件会加入到这个循环中被处理。但如果前一个事件太过耗时,下一个事件等待时间太长超出特定时间,这时才会产生 ANR。所以 Looper 死循环并不是产生 ANR 的原因。

消息队列

enqueueMessage 是消息的入队方法。Handler 在进行线程间通信时,会调用 sendMessage 将消息发送到接收消息的线程的消息队列中,消息队列调用 enqueueMessage 将消息入队

消息入队分为 3 步:

① 将入队的时间绑定在 when 属性上

② 遍历链表,通过比较 when 找到插入位置

③ 将 msg 插入到链表中

异步消息和同步屏障(优先级)

sendMessage 方法发送的是同步消息。

异步消息需要和同步屏障配合使用,来提升消息的优先级。

当事件循环检测到同步屏障时,之后的行为不再像之前那样根据 when 的值一个个取消息,而是遍历整个消息队列,查找到异步消息取出并执行。

插入了一个同步屏障,不移除,会发生什么事呢?

同步屏障是用来“拦住”同步消息,处理异步消息的。如果同步屏障不移除,消息队列里的异步消息会一个一个被取出处理,直到异步消息被取完。如果此时队列中没有异步消息了,则线程会阻塞,队列中的同步消息永远不会执行。所以同步屏障要及时移除。

为什么使用 Handler 会有内存泄漏问题呢?该如何解决呢?

一个对象本来应该在一个短的生命周期中被回收,结果被一个长生命周期的对象引用,导致无法回收。 Handler 的内存泄漏其实是内部类持有外部类引用导致的。 形成方式有两种:

匿名内部类持有外部类引用

非静态内部类持有外部类引用

Handler 是如何与线程关联的?

实例化 Handler 的时候 Handler 会去检查当前线程的 Looper 是否存在,如果不存在则会报异常,也就是说在创建 Handler 之前一定需要先创建 Looper

一般来说都是在主线程中使用而主线程已经为我们创建好了 Looper

如果在子线程中使用 需要 初始化 Looper 调用 Looper.prepare(); 以及调用 loop循环 Looper.loop();

Looper 提供了 Looper.prepare() 方法来创建 Looper ,并且会借助 ThreadLocal 来实现与当前线程的绑定功能。Looper.loop() 则会开始不断尝试从 MessageQueue 中获取 Message , 并分发给对应的 Handler

也就是说 Handler 跟线程的关联是靠 Looper 来实现的。

Handler 发出去的消息是谁管理的?

Handler 提供了一些列的方法让我们来发送消息,如 send()系列 post()系列 。

不过不管我们调用什么方法,最终都会走到 MessageQueue.enqueueMessage(Message,long) 方法。消息的管理者 MessageQueue 也就露出了水面

MessageQueue 顾明思议,就是个队列,负责消息的入队出队。

消息又是怎么回到 handleMessage() 方法的?

Looper.loop() 是个死循环,会不断调用 MessageQueue.next() 获取 Message ,并调用 msg.target.dispatchMessage(msg) 回到了 Handler 来分发消息,以此来完成消息的回调

HandlerThread 和 IdleHandler

子线程是需要我们通过 Looper.prepare()和 Looper.loop()手动开启事件循环的。HandlerThread 其实就帮我们做了这件事,它是一个实现了事件循环的线程。我们可以在这个线程中做一些 IO 耗时操作。

和同步屏障一样是一种特殊的”消息"。不同于 Message,它是一个接口

Idle 是空闲的意思。与同步屏障不同,同步屏障是提高异步消息的优先级使其优先执行,IdleHandler 是事件循环出现空闲的时候来执行。

这里的“空闲”主要指两种情况

(1)消息队列为空

(2)消息队列不为空但全部是延时消息,也就是 msg.when > now

利用这一特性,我们可以将一些不重要的初始化操作放在 IdleHandler 中执行,以此加快 app 启动速度;由于 View 的绘制是事件驱动的,我们也可以在主线程的事件循环中添加一个 IdleHandler 来作为 View 绘制完成的回调,

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值