Handler 常见的问题以及底层原理分析

1. Handler 作用

  1. 原理 > Handler通过sendMessage发送消息,将消息放入MessageQueue中,在MessageQueue中通过时间的维度来进行排序,Looper通过调用loop方法不断的从MessageQueue中获取消息,执行HandlerdispatchMessage,最后调用handleMessage方法。
  1. 来源 > 因为Android中主线程是不能进行耗时操作的,子线程是不能进行更新UI。所以就有handler,它的作用就是实现线程之间的通信。 handler整个流程中,主要有四个对象,handlerMessage, MessageQueue,Looper。当APP应用启动创建的时候,就会在主线程中创建handler对象, 我们通过要传送的消息保存到Message中,handler通过调用sendMessage方法将Message发送到MessageQueue中,Looper对象就会不断的调用loop ()方法 不断的从MessageQueue中取出Message交给handler进行处理。来实现线程之间的通信。

2. Handler、Looper、MessageQueue、线程的关系

  • 一个线程只会有一个Looper对象,所以线程和Looper是一一对应的。MessageQueue对象是在new Looper的时候创建的,所以LooperMessageQueue是一一对应的。
    Handler的作用只是将消息加到MessageQueue中,并后续取出消息后,根据消息的target字段分发给当初的那个handler,所以Handler对于Looper是可以 多对一的,也就是多个Hanlder对象都可以用同一个线程、同一个Looper、同一个MessageQueue
  • 总结:LooperMessageQueue、线程是一一对应关系,而他们与Handler是可以一对多

3. Handler 的底层实现原理?

  1. 理解handler的实现原理,最重要的是理解Looper的实现原理,Looper才是实现handler机制的核心。任何一个handler在使用sendMessage或者post时候,都会先构造一个Message,并把自己放到message中,然后把Message放到对应的LooperMessageQueueLooper通过控制MessageQueue来获取message执行其中的handler或者runnable。 要在当前线程中执行handler指定操作,必须要先看当前线程中有没有looper,如果有looper,handler就会通过sendMessage,或者post先构造一个message,然后把message放到当前线程的looper中,looper会在当前线程中循环取出message执行,如果没有looper,就要通过looper.prepare()方法在当前线程中构建一个looper,然后主动执行looper.loop()来实现循环。
  2. 梳理四条关键点:
    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方法的不同在于:
    postrunnable会直接在callback中调用run方法执行,而sendMessage方法要用户主动重写mCallback或者handleMessage方法来处理。

5. Looper会一直消耗系统资源吗?

  • Looper不会一直消耗系统资源,当LooperMessageQueue中没有消息时,或者定时消息没到执行时间时,当前持有Looper的线程就会进入阻塞状态。looper阻塞肯定跟消息出队有关

6. Android的Handle机制,Looper关系,主线程的Handler是怎么判断收到的消息是哪个Handler传来的?

  • handler在sendMessage的时候会构建一个Message对象,并且把自己放在Message的target里面,这样的话Looper就可以根据Message中的target来判断当前的消息是哪个handler传来的。

7. Handler机制流程、Looper中延迟消息谁来唤醒Looper?

  • 在消息出队的for循环队列中会调用到下面的方法。
  nativePollOnce(ptr, nextPollTimeoutMillis);
  • 如果是延时消息,会在被阻塞nextPollTimeoutMillis时间后被叫醒,nextPollTimeoutMillis就是消息要执行的时间和当前的时间差。

8. Handler是怎么引起内存泄漏的?如何解决?

    1. 在Handler的handleMessage方法中(或者是run方法)处理消息,如果这个是一个延时消息,会一直保存在主线程的消息队列里,并且会影响系统对Activity的回收,造成内存泄露
    1. a. 在Handler的handleMessage方法中(或者是run方法)处理消息,如果这个是一个延时消息,会一直保存在主线程的消息队列里,并且会影响系统对Activity的回收,造成内存泄露
    1. 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进来的时候会打破阻塞继续执行。

你可能感兴趣的:(Handler 常见的问题以及底层原理分析)