首页 » 技术分享 » 不知庐山真面目只缘身在此山中,讲解一下Handler

不知庐山真面目只缘身在此山中,讲解一下Handler

 

o(╥﹏╥)o今天太困了,啥也不想干。就把之前收集的资料拿出来温习一下刚好和大家共享下

之前在GitHub
上更新了些关于面试类的文章,含有从基础到进阶。含有BATJ.字节跳动面试专题,算法专题,高端技术专题,混合开发专题,java面试专题,Android,Java小知识,到性能优化.线程.View.OpenCV.NDK等应有尽有。还有辅之相关的视频+学习笔记

请查看完整的PDF版

更多完整项目下载。未完待续。源码。图文知识后续上传github。
可以点击关于我联系我获取

Handler

1、谈谈消息机制 Handler 作用 ?

负责跨线程通信,这是因为在主线程不能做耗时操作,而子线程不能更新 UI,所以当子线程中进行耗时操作后需要更新 UI时,通过 Handler 将有关 UI 的操作切换到主线程中执行。

具体分为四大要素:

  • Message(消息): 需要被传递的消息,消息分为硬件
    产生的消息(如按钮、触摸)和软件生成的消息。
  • MessageQueue(消息队列): 负责消息的存储与管
    理,负责管理由 Handler 发送过来的 Message。读取
    会自动删除消息,单链表维护,插入和删除上有优势。
    在其 next()方法中会无限循环,不断判断是否有消息,
    有就返回这条消息并移除。
  • Handler(消息处理器): 负责 Message 的发送及处
    理。主要向消息池发送各种消息事件
    Handler.sendMessage())和处理相应消息事件
    Handler.handleMessage()),按照先进先出执行,
    内部使用的是单链表的结构。
  • Looper(消息池): 负责关联线程以及消息的分发,在
    该线程下从 MessageQueue 获取 Message,分发给Handler,Looper 创建的时候会创建一个MessageQueue,调用 loop()方法的时候消息循环开始,其中会不断调用 messageQueue 的 next()方法,当有消息就处理,否则阻塞在 messageQueue 的next()方法中。当 Looperquit()被调用的时候会调用
    messageQueuequit(),此时 next()会返回 null,然
    loop()方法也就跟着退出。

具体流程如下:

  • 在主线程创建的时候会创建一个 Looper,同时也会在在
    Looper 内部创建一个消息队列。而在创键 Handler 的
    时候取出当前线程的 Looper,并通过该 Looper 对象获
    得消息队列,然后 Handler 在子线程中通过MessageQueue.enqueueMessage 在消息队列中添
    加一条 Message
  • 通过 Looper.loop() 开启消息循环不断轮询调用
    MessageQueue.next(),取得对应的 Message 并且
    通过 Handler.dispatchMessage 传递给 Handler,最
    终调用 Handler.handlerMessage 处理消息。

2、一个线程能否创建多个 Handler,Handler 跟 Looper 之间的对应关系 ?

  • 一个 Thread 只能有一个 Looper,一个 MessageQueen,可以有多个 Handler
  • 以一个线程为基准,他们的数量级关系是: Thread(1) :
    Looper(1) : MessageQueue(1) : Handler(N)

3、软引用跟弱引用的区别

  • 软引用(SoftReference): 如果一个对象只具有软引用,则
    内存空间充足时,垃圾回收器就不会回收它;如果内存空间不
    足了,就会回收这些对象的内存。只要垃圾回收器没有回收
    它,该对象就可以一直被程序使用。
  • 弱引用(WeakReference): 如果一个对象只具有弱引用,那
    么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用
    的对象,不管当前内存空间足够与否,都会回收它的内存。
  • 两者之间根本区别在于: 只具有弱引用的对象拥有更短暂的生
    命周期,可能随时被回收。而只具有软引用的对象只有当内存
    不够的时候才被回收,在内存足够的时候,通常不被回收

4、Handler 引起的内存泄露原因以及最佳解决方案

为什么会导致泄露:
Handler 允许我们发送延时消息,如果在延时期间用户
关闭了 Activity,那么该 Activity 会泄露。 这个泄露
是因为 Message 会持有 Handler,而又因为 Java 的
特性,内部类会持有外部类,使得 Activity 会被
Handler 持有,这样最终就导致 Activity 泄露。

怎么解决:
将 Handler 定义成静态的内部类,在内部持有
Activity 的弱引用,并在 AcitivityonDestroy()
调用 handler.removeCallbacksAndMessages(null)
及时移除所有消息。

5、为什么系统不建议在子线程访问 UI?

Android 的 UI 控件不是线程安全的,如果在多线程中并发访问
可能会导致 UI 控件处于不可预期的状态

这时你可能会问为何系统不对 UI 控件的访问加上锁机制呢?因为:

  • 加锁机制会让 UI 访问逻辑变的复杂
  • 加锁机制会降低 UI 的访问效率,因为加锁会阻塞某些线
    程的执行

6、Looper 死循环为什么不会导致应用卡死?

  • 主线程的主要方法就是消息循环,一旦退出消息循环,那么你的应用也就退出了,Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不
    会产生 ANR 异常。
  • 造成 ANR 的不是主线程阻塞,而是主线程的 Looper消息处理过程发生了任务阻塞,无法响应手势操作,不能及时刷新 UI。
  • 阻塞与程序无响应没有必然关系,虽然主线程在没有消息可处理的时候是阻塞的,但是只要保证有消息的时候能够立刻处
    理,程序是不会无响应的。

7、使用 Handler 的 postDealy 后消息队列会有什么变化?

如果队列中只有这个消息,那么消息不会被发送,而是计算到
时唤醒的时间,先将 Looper 阻塞,到时间就唤醒它。但如果此时要加入新消息,该消息队列的对头跟 delay 时间相比更长,
则插入到头部,按照触发时间进行排序,队头的时间最小、队
尾的时间最大

8丶子线程直接 new 一个 Handler 吗?怎么做?

不可以,因为在主线程中,Activity 内部包含一个 Looper 对
象,它会自动管理 Looper,处理子线程中发送过来的消息。而
对于子线程而言,没有任何对象帮助我们维护 Looper 对象,所以需要我们自己手动维护。所以要在子线程开启 Handler 要先
创建 Looper,并开启 Looper 循环

9、Message 可以如何创建?哪种效果更好,为什么?

可以通过三种方法创建:

  • 直接生成实例 Message m = new Message
  • 通过 Message m = Message.obtain
  • 通过 Message m = mHandler.obtainMessage()

后两者效果更好,因为 Android 默认的消息池中消息数量是 10,而后
两者是直接在消息池中取出一个 Message 实例,这样做就可以避免多
生成 Message 实例。

请查看完整的PDF版

更多完整项目下载。未完待续。源码。图文知识后续上传github。
可以点击关于我联系我获取

转载自原文链接, 如需删除请联系管理员。

原文链接:不知庐山真面目只缘身在此山中,讲解一下Handler,转载请注明来源!

0