前言: Handler 机制应该是网上讲解最多的一种机制(没有之一), 本篇用通俗易懂的语言来介绍一下 Handler 机制, 让大家可以更好的理解
什么是 Handler 机制?
Handler 机制是 AndroidSDK 提供的一个非常重要的处理异步消息的机制, 主要是由 HandlerLooperMessage 和 MessageQueue 组成, Handler 只是消息处理机制的一部分
Message: 消息(分为硬件产生的消息和软件产生的消息)
MessageQueue: 消息队列, 主要是向消息池投递消息 (MessageQueue.enqueueMessage) 和取走消息池中的消息(MessageQueue.next)
Handler: 主要功能是向消息池发送消息 (Handler.sendMessage) 和处理消息(Handler.handleMessage)
Looper: 不停的循环执行(Looper.loop), 从 MessageQueue 中取出 Message 并发送给 Handler
分析上述各部分:
Message: 什么是硬件消息和软件消息呢? 硬件消息就是我们滑动触摸点击按钮等等, 软件消息就是我们主动 new Message 发送出去的 Message 实现了 Parcelable 接口封装消息数据, 所以他是存在于内存中的一个实体 (类) 如果需要封装到消息中去就必须实现这一接口
MessageQueue: 相当于一个容器, 消息池上述看到了. next 应该猜到是链表形式, 实际上确实是单链表维护, 在插入和删除上有优势在其 next()中会无限循环, 不断的判断是否有消息, 有就返回这条消息并移除
Looper:Looper 创建的时候会创建一个 MessageQueue, 它们两个是一一对应的关系调用 Looper.loop()的时候消息循环开始, 不断地调用 MessageQueue 的 next()方法, 当有消息就处理, 否则就堵塞在 next()方法中 loop()跟 MessageQueue 的 next()一样都是死循环 (源码可见 for(;;)) 退出时调用 Looper.quit(), 它会调用 MessageQueue 的 quit()方法, 此时 next 会返回 null, 然后 loop()方法也跟着退出
Handler: 在主线程构造 Handler,new Handler()里调用了 Looper.myLooper()这个方法, 这个方法是获取当前线程的 Looper 的在其他线程调用 sendMessage()时主线程的 MessageQueue 会插入一条 Message, 然后被 Looper 使用, 在 Looper 的 loop()中通过回调 msg.target.dispatchMessage(msg); 发送给 HandlerHandler 跟 Looper 的关系是多对一
一段 Looper 的源码片段:
解释完各部分的分工那来总结一下他们之间的关系: Handler 负责发送消息 (Message) 到 MessageQueue 中, Looper 负责循环的接收 MessageQueue 中的消息通过回调方法返还给 Handler 自己本身
Handler 机制流程图
容易忽略的 Message, 使用时应注意的地方有哪些?
Message 在 Handler 机制中看似很不起眼, 但至关重要它封装了任务携带的信息和该任务的 handler, 使用时应注意:
Message 可以通过 new 来获取, 但通常使用 Message.obtain()或 Handler.obtainMessage()方法来从消息池中获取空消息对象, 可以节省资源!
如果 Message 只需要携带简单的 int 型数据, 优先使用 arg1 和 arg2 来传递数据, 比 Bundle 节省内存
使用 Message.what 来标识信息便于处理 Message
最后如果需要从工作线程返回很多数据信息再借助 Bundle 对象将数据集中到一起放在 obj 属性中, 返回到主线程处理
Looper 源码简述:
上面说了那么多 Looper 的方法, 但在 Activity 中使用 Handler 时没看到过 Looper 的影子啊, 原来 Activity 内部包含了一个 Looper 对象, 它会自动管理 Looper 并处理子线程中发送过来的消息前面说到过, 初始化 Handler 时在 Handler 的构造函数中会把当前线程的 Looper 与 Handler 关联, 所以在 Activity 中无需显式的使用 Looper
但在子线程中则需要我们自己维护 Looper
源码简述
网上介绍 Handler 机制的文章解释最详细的就是 Looper, 会附带源码每一篇都会解释的很透彻这里就简单的总结一下 Looper, 详细请去查阅 Looper 源码
可以看出 Prepare()的工作方式核心就是将 Looperd 对象定义为 ThreadLocal, 将唯一的 Looper 对象添加到 ThreadLocal 中
Looper 在构造方法中创建了一个 MessageQueue 和当前线程 Thread
那什么是 TheadLocal 呢?
ThrealLocal 是一个泛型类
工作原理: ThreadLocal 存储数据时先获取当前线程, 然后通过当前线程来创建 Values, 如果没有 Values 就 new 一个 Values 实例然后存储传进来的 Value 获取的时候也是根据当前线程的 Values 来获取的
它的 get()和 set()方法如下图:
使用场景: 数据是以线程为作用域并且不同线程具有不同的数据副本时可以使用
最后说一下为什么 Handler 机制会造成内存泄露
因为非静态内部类导致的!
我们知道非静态内部类默认就会持有外部类的引用, 当非静态内部类对象的生命周期比外部类还长就会导致内存泄露
上面介绍 Message 时说过 mHandler 会作为成员变量保存在发送的消息 msg 中, 所以 msg 就会持有 mHandler 的引用, 而 mHandle 是 MainActivity 的非静态内部类实例, 所以 mHandler 就持有 MainActivity 的引用 msg 间接持有 MainActivity 的引用 msg 发送到消息队列 (MessageQueue) 中等待 Looper 轮询处理当 MainActivity 退出后, msg 可能还存在消息队列中未处理或正在处理这样就会导致 MainActivity 无法被回收, 以致发生 MainActivity 的内存泄露
解决办法: 使用静态内部类 + 弱引用的方式
mHandler 通过弱引用的方式持有 MainActivity, 当 GC 执行垃圾回收时遇到 MainActivity 就会回收并释放所占的内存单元, 避免内存泄露 msg 还可能存在 MessageQueue 中, 所以在 MainActivity 销毁时将 mHandler 的回调和发送的消息给移除掉
结束
到此 Handler 机制的内容就算讲完了, 但本篇只是用比较容易理解的方式介绍了一下 Handler 机制, Handler 机制在 Android 开发中随处可见也很重要大家很有必要去深入研究一下, 去看一下里面的源码或结合一些讲解源码的文章自己理解一下希望能帮到大家, 有错的地方请提出
来源: http://www.jianshu.com/p/b63cdff5e661