面试:Handler 的工作原理是怎样的?
面试场景
平时开发用到其他线程吗?都是如何处理的?
基本都用 RxJava 的线程调度切换,嗯对,就是那个 observeOn 和 subscribeOn 可以直接处理,比如网络操作,RxJava 提供了一个叫 io 线程的处理。
在 RxJava 的广泛使用之前,有使用过其他操作方式吗?比如 Handler 什么的?
当然用过呀。
那你讲讲 Handler 的工作原理吧。
Handler 工作流程基本包括 Handler、Looper、Message、MessageQueue 四个部分。但我们在日常开发中,经常都只会用到 Handler 和 Message 两个类。Message 负责消息的搭载,里面有个 target 用于标记消息,obj 用于存放内容,Handler 负责消息的分发和处理。
一般在开发中是怎么使用 Handler 的?
官方不允许在子线程中更新 UI,所以我们经常会把需要更新 UI 的消息直接发给处理器 Handler,通过重写 Handler 的 handleMessage() 方法进行 UI 的相关操作。
那使用中就没什么需要注意的吗?
有,Handler 如果设置为私有变量的话,Android Studio 会报警告,提示可能会造成内存泄漏,这种情况可以通过设置为静态内部类 + 弱引用,或者在 onDestroy() 方法中调用 Handler.removeCallbacksAndMessages(null) 即可避免;
正文
总的来说这位面试的童鞋答的其实还是没那么差,不过细节程度还不够,所以南尘就来带大家一起走进 Handler。
Handler 工作流程浅析
异步通信准备 => 消息入队 => 消息循环 => 消息处理
异步通信准备
假定是在主线程创建 Handler,则会直接在主线程中创建处理器对象Looper、消息队列对象MessageQueue和 Handler 对象。需要注意的是,Looper和MessageQueue均是属于其 创建线程 的。Looper对象的创建一般通过Looper.prepareMainLooper()和Looper.prepare()两个方法,而创建Looper对象的同时,将会自动创建MessageQueue,创建好MessageQueue后,Looper将自动进入消息循环。此时,Handler自动绑定了主线程的Looper和MessageQueue。消息入队
工作线程通过Handler发送消息Message到消息队列MessageQueue中,消息内容一般是 UI 操作。发送消息一般都是通过Handler.sendMessage(Message msg)和Handler.post(Runnabe r)两个方法来进行的。而入队一般是通过MessageQueue.enqueueeMessage(Message msg,long when)来处理。消息循环
主要分为「消息出队」和「消息分发」两个步骤,Looper会通过循环 取出 消息队列MessageQueue里面的消息Message,并 分发 到创建该消息的处理者Handler。如果消息循环过程中,消息队列MessageQueue为空队列的话,则线程阻塞。消息处理
Handler接收到Looper发来的消息,开始进行处理。
对于 Handler ,一些需要注意的地方
- 1 个线程
Thread只能绑定 1个循环器Looper,但可以有多个处理者Handler - 1 个循环器
Looper可绑定多个处理者Handler - 1 个处理者
Handler只能绑定 1 个 1 个循环器Looper
常规情况下,这些相关对象是怎么创建的?
前面我们说到 Looper 是通过 Looper.prepare() 和 Looper.prepareMainLooer() 创建的,我们不妨看看源码里面到底做了什么。

我们不得不看看 Looper 的构造方法都做了什么。

显而易见,确实在创建了 Looper 对象的时候,自动创建了消息队列对象 MessageQueue。
而 Looper.prepareMainLooper() 从名称也很容易看出来,是直接在主线程内创建对象了。而在我们日常开发中,经常都是在主线程使用 Handler,所以导致了很少用到 Looper.prepare() 方法。
而生成 Looper 和 MessageQueue 对象后,则自动进入消息循环:Looper.loop(),我们不妨再看看里面到底做了什么?

截图中的代码比较简单,大家应该不难看懂,我们再看看如何通过 MessageQueue.next()来取消息设置阻塞状态的。

我们取消息采用了一个无限 for 循环,当没有消息的时候,则把标记位 nextPollTimeOutMillis 设置为 -1,在进行下一次循环的时候,通过 nativePollOnce() 直接让其处于线程阻塞状态。
再看看我们的消息分发是怎么处理的,主要看上面的 msg.target.dispatchMessage(msg) 方法。

原来 msg.target 返回的是一个 Handler 对象,我们直接看看 Handler.dipatchMessage(Message msg) 做了什么。

总结:
- 在主线程中
Looper对象自动生成,无需手动生成。而在子线程中,一定要调用Looper.prepare()创建Looper对象。如果在子线程不手动创建,则无法生成Handler对象。- 分发消息给
Handler的过程为:根据出队消息的归属者,通过dispatchMessage(msg)进行分发,最终回调复写的handleMessage(Message msg)。- 在消息分发
dispatchMessage(msg)方法中,会进行 1 次发送方式判断:
1. 若msg.callback属性为空,则代表使用了post(Runnable r)发送消息,则直接回调Runnable对象里面复写的run()。
2. 若msg.callback属性不为空,则代表使用了sendMessage(Message msg)发送消息,直接回调复写的handleMessage(msg)。
常规的消息 Message 是如何创建的?
我们经常会在 Handler 的使用中创建消息对象 Message,创建方式也有两个 new Message() 或者 Message.obtain()。我们通常都更青睐于 Message.obtain() 这种方式,因为这样的方式,可以有效避免重复创建 Message 对象。实际上在代码中也是显而易见的。

Handler 的另外一种使用方式
前面主要讲解了 Handler.sendMessage(Message msg) 这种常规使用方式,实际上,我们有时候也会用 Handler.post(Runnable r) 进行处理,我们当然应该看看里面是怎么处理的。

从官方注释可以看到,这会直接将 Runnable 对象加到消息队列中,我们来看看 `getPostMessage(r) 到底做了什么。

我们上面的分析是对的。在 getPostMessage(Runnable r) 方法中,我们除了通过 Message.obtain() 方法来创建消息对象外,专门把 Runnable 对象赋值给了 callback,这样才用了上面做消息分发的时候,通过这个标记来判断是用的 post() 还是 sendMessage() 方式。
到底是如何发消息的?
一直在说通过 sendMessage() 方式来发消息,到底这个消息是怎么发送的呢?

直接看 sendMessageAtTime()。

enqueueMessage() 里面做了什么?

至此,你大概明白了两种方式的区别了。
写在最后
本次内容可能讲的比较多和乱,还望大家跟着到源码中一步一步分析,最困难的时候,就是提升最大的时候!
面试:Handler 的工作原理是怎样的?的更多相关文章
- 【原创】源码角度分析Android的消息机制系列(六)——Handler的工作原理
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 先看Handler的定义: /** * A Handler allows you to send and process {@link Mes ...
- Android 异步通信:图文详解Handler机制工作原理
前言 在Android开发的多线程应用场景中,Handler机制十分常用 今天,我将图文详解 Handler机制 的工作原理,希望你们会喜欢 目录 1. 定义 一套 Android 消息传递机制 2. ...
- Handler消息传递机制——Handler、Loop、MessageQueue的工作原理
为了更好地理解Handler的工作原理,先介绍一下与Handler一起工作的几个组件. Message:Handler接收和处理的消息对象. Looper:每个线程只能拥有一个Looper.它的loo ...
- Handler系列之原理分析
上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...
- 为什么要有handler机制?handler机制的原理
为什么要有handler机制? 在Android的UI开发中,我们经常会使用Handler来控制主UI程序的界面变化.有关Handler的作用,我们总结为:与其他线程协同工作,接收其他线程的消息并通过 ...
- Android 为什么要有handler机制?handler机制的原理
为什么要有handler机制? 在Android的UI开发中,我们经常会使用Handler来控制主UI程序的界面变化.有关Handler的作用,我们总结为:与其他线程协同工作,接收其他线程的消息并通过 ...
- 面试官:你分析过mybatis工作原理吗?
Mybatis工作原理也是面试的一大考点,必须要对其非常清晰,这样才能怼回去.本文建立在Spring+SpringMVC+Mybatis整合的项目之上. 我将其工作原理分为六个部分: 读取核心配置文件 ...
- Java面试必问之线程池的创建使用、线程池的核心参数、线程池的底层工作原理
一.前言 大家在面试过程中,必不可少的问题是线程池,小编也是在面试中被问啥傻了,JUC就了解的不多.加上做系统时,很少遇到,自己也是一知半解,最近看了尚硅谷阳哥的课,恍然大悟,特写此文章记录一下!如果 ...
- JVM工作原理和特点(一些二逼的逼神面试官会问的问题)
作为一种阅读的方式了解下jvm的工作原理 ps:(一些二逼的逼神面试官会问的问题) JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.exe来完毕,通过以下4步来完毕JVM环境. ...
随机推荐
- stm32开发之串口的调试
总的函数如下 void USART1Configuration(void){ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Peri ...
- Java中的表达式运算
1.问题背景 以下代码运行的结果为: A.a的值:8 b的值:7 B.a的值:8 b的值:8 C.a的值:9 b的值:7 D.a的值 ...
- 如何创建Filter的属性页
本篇文档我们将要讲述如何给一个filter创建一个属性页,通过CBasePropertyPage基类.这篇文档的实例代码演 示了创建属性页的步骤,这里我们假设我们要创建属性页的视频filter支持饱和 ...
- class-k近邻算法kNN
1 k近邻算法2 模型2.1 距离测量2.2 k值选择2.3 分类决策规则3 kNN的实现--kd树3.1 构造kd树3.2 kd树搜索 1 k近邻算法 k nearest neighbor,k-NN ...
- Error Correct System CodeForces - 527B
Ford Prefect got a job as a web developer for a small company that makes towels. His current work ta ...
- Dynamics 365 Online-60000 API Limits
最近,Dynamics 365官方文档有最新的发布,就是从2018.3.19开始,会对API请求的数目有个限制:在连续的5分钟里,一个连接用户,针对一个CRM Organization,API请求数不 ...
- Struts2【配置】知识要点
前言 上篇Struts博文已经讲解了Struts的开发步骤以及执行流程了.....对Struts的配置文件有了了解.....本博文继续讲解Struts在配置的时候一些值得要学习的细节... 通配符 为 ...
- 关于CoordinatorLayout的用法——复杂交互的克星
好久没有写博客了,主要还是任务过多哈.在开发的过程当中,也记录了很多东西,但是技术这个事吧,其实,时效性真的事非常强--就比如说,你昨天还津津乐道的一个难点解决方案,你过个几天再回过头去看它,就会有一 ...
- Error:Execution failed for task ':app:processDebugGoogleServices'. > No matching client found for package name 'com.fortythree.sos.flashlight'
Q:导入json文件时的包名不对 A:包名存在的位置是app build gradle中的applicationID
- Angular2 - 概述
*Hi DAI, 我想学习 Angular2, 我应该怎么开始? 关于学习 Angular2, 我认为你应该按照下面的列表 概述: 在您为 Angular2 应用程序编写第一个代码之前, 这将为您提供 ...