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进来的时候会打破阻塞继续执行。