1. Handler
作用
- 原理
>
Handler
通过sendMessage
发送消息,将消息放入MessageQueue
中,在MessageQueue
中通过时间的维度来进行排序,Looper
通过调用loop
方法不断的从MessageQueue
中获取消息,执行Handler
的dispatchMessage
,最后调用handleMessage
方法。
- 来源
>
因为Android中主线程
是不能进行耗时操作的,子线程
是不能进行更新UI。所以就有handler,它的作用就是实现线程之间的通信。 handler
整个流程中,主要有四个对象,handler
,Message
, MessageQueue
,Looper
。当APP应用启动创建的时候,就会在主线程中创建handler
对象, 我们通过要传送的消息保存到Message
中,handler
通过调用sendMessage
方法将Message
发送到MessageQueue
中,Looper
对象就会不断的调用loop ()
方法 不断的从MessageQueue
中取出Message
交给handler
进行处理。来实现线程之间的通信。
2. Handler、Looper、MessageQueue、线程的关系
- 一个线程只会有一个
Looper
对象,所以线程和Looper
是一一对应的。MessageQueue
对象是在new Looper的时候创建的,所以Looper
和MessageQueue
是一一对应的。
Handler
的作用只是将消息加到MessageQueue中,并后续取出消息后,根据消息的target
字段分发给当初的那个handler
,所以Handler
对于Looper
是可以 多对一的,也就是多个Hanlder对象都可以用同一个线程、同一个Looper
、同一个MessageQueue
。
- 总结:
Looper
、MessageQueue
、线程是一一对应关系,而他们与Handler
是可以一对多的
3. Handler 的底层实现原理?
- 理解
handler
的实现原理,最重要的是理解Looper
的实现原理,Looper
才是实现handler
机制的核心。任何一个handler
在使用sendMessage
或者post
时候,都会先构造一个Message
,并把自己放到message
中,然后把Message
放到对应的Looper
的MessageQueue
,Looper
通过控制MessageQueue
来获取message
执行其中的handler
或者runnable
。 要在当前线程中执行handler
指定操作,必须要先看当前线程中有没有looper
,如果有looper,handler
就会通过sendMessage
,或者post
先构造一个message
,然后把message
放到当前线程的looper
中,looper
会在当前线程中循环取出message
执行,如果没有looper
,就要通过looper.prepare()
方法在当前线程中构建一个looper
,然后主动执行looper.loop()
来实现循环。
- 梳理四条关键点:
1、每一个线程中最多只有一个Looper,通过ThreadLocal来保存,Looper中有Message队列,保存handler并且执行handler发送的message。
2、在线程中通过Looper.prepare()来创建Looper,并且通过ThreadLocal来保存Looper,每一个线程中只能调用一次Looper.prepare(),也就是说一个线程中最多只有一个Looper,这样可以保证线程中Looper的唯一性。
3、handler中执行sendMessage或者post操作,这些操作执行的线程是handler中Looper所在的线程,和handler在哪里创建没关系,和Handler中的Looper在那创建有关系。
4、一个线程中只能有一个Looper,但是一个Looper可以对应多个handler,在同一个Looper中的消息都在同一条线程中执行。
4. Handler机制,sendMessage和post(Runnable)的区别?
-
post
方法和handleMessage
方法的不同在于:
post
的runnable
会直接在callback
中调用run
方法执行,而sendMessage
方法要用户主动重写mCallback
或者handleMessage
方法来处理。
5. Looper会一直消耗系统资源吗?
-
Looper
不会一直消耗系统资源,当Looper
的MessageQueue
中没有消息时,或者定时消息没到执行时间时,当前持有Looper
的线程就会进入阻塞状态。looper
阻塞肯定跟消息出队有关
6. Android的Handle机制,Looper关系,主线程的Handler是怎么判断收到的消息是哪个Handler传来的?
- handler在sendMessage的时候会构建一个Message对象,并且把自己放在Message的target里面,这样的话Looper就可以根据Message中的target来判断当前的消息是哪个handler传来的。
7. Handler机制流程、Looper中延迟消息谁来唤醒Looper?
nativePollOnce(ptr, nextPollTimeoutMillis);
- 如果是延时消息,会在被阻塞
nextPollTimeoutMillis
时间后被叫醒,nextPollTimeoutMillis
就是消息要执行的时间和当前的时间差。
8. Handler是怎么引起内存泄漏的?如何解决?
- 在Handler的handleMessage方法中(或者是run方法)处理消息,如果这个是一个延时消息,会一直保存在主线程的消息队列里,并且会影响系统对Activity的回收,造成内存泄露
- a. 在Handler的handleMessage方法中(或者是run方法)处理消息,如果这个是一个延时消息,会一直保存在主线程的消息队列里,并且会影响系统对Activity的回收,造成内存泄露
- b. 匿名内部类导致的泄露改为匿名静态内部类,并且对上下文或者Activity使用弱引用。
9. handler机制中如何确保Looper的唯一性?
- Looper是保存在线程的ThreadLocal里面的,使用Handler的时候要调用Looper.prepare()来创建一个Looper并放在当前的线程的ThreadLocal里面。
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));
}
- 可以看到,如果多次调用
prepare
的时候就会报Only one Looper may be created per thread
,所以这样就可以保证一个线程中只有唯一的一个Looper
10. Handler 是如何能够线程切换,发送Message的?
-
handler
的执行跟创建handler
的线程无关,跟创建looper的线程
相关,加入在子线程中创建一个Handler
,但是Handler
相关的Looper
是主线程的,这样,如果handler
执行post
一个post
,或者sendMessage
,最终的handle Message
都是在主线程中执行的。
11. Handler是如何切换线程的
- 每个
Looper
都运行在对应的线程,所以不同的Looper
调用的dispatchMessage
方法就运行在其所在的线程了。
12. 为什么主线程的Looper是一个死循环,但是却不会ANR
- 因为当Looper处理完所有消息的时候会进入阻塞状态,当有新的Message进来的时候会打破阻塞继续执行。
网友评论