面试: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环境. ...
随机推荐
- Struts2实现文件上传(二)
Struts2实现文件上传 文件上传页面 file.jsp: <%@ page language="java" import="java.util.*" ...
- 获取MySql每一列的数据类型和长度默认值等信息
如何获取MySql表中各个列的数据类型? show columns from tablename 返回结果如下: id int(11) NO PRI auto_incr ...
- eval及json的理解
一,案例一 <br><br> 复制代码 var str = '{"name": "hanzichi", "age": ...
- http协议的补充二
一,浏览器到服务器request 1.1,浏览器里面的内容 请求(浏览器->服务器) GET /day09/hello HTTP/1.1 Host: localhost:8080 User-Ag ...
- spring+jidi读取property的配置文件
在Spring项目中,你可能需要从properties文件中读入配置注入到bean中,例如数据库连接信息,memcached server的地址端口信息等,这些配置信息最好独立于jar包或者war包, ...
- ASP.NET VS2013 Office 转 PDF
本文适用于VS2013 项目中的Word转换为PDF.Excel转换为PDF.PPT转换为PDF 0.一种更加简单方便的方法 1.本页所用的方法在本机测试时基本不会出现问题,只是偶尔PPT转PDF失败 ...
- Linux 的特殊变量(2)
1.echo $? 输出结果为0 表示命令执行成功 场景:判断上一个命令是否成功 扩展 0:成功 2 :权限拒绝 1~125:表示运行失败 126:找到命令,但是无法执行 127:未找到要运行的命令 ...
- Struts2【拦截器】就是这么简单
什么是拦截器 拦截器Interceptor.....拦截器是Struts的概念,它与过滤器是类似的...可以近似于看作是过滤器 为什么我们要使用拦截器 前面在介绍Struts的时候已经讲解过了,Str ...
- SPOJ1825:Free tour II
题意 luogu的翻译 给定一棵n个点的树,树上有m个黑点,求出一条路径,使得这条路径经过的黑点数小于等于k,且路径长度最大 Sol 点分治辣 如果是等于\(k\)的话,开个桶取\(max\)就好了 ...
- [USACO5.4]奶牛的电信Telecowmunication
裸的最小割,拆点时要考虑清楚到底是怎么连 如果之前i->i+n,之后又x->y+n,显然出不了解 所以可以改为i+n->i 如果要输出方案 考虑每一个一个点,如果删去这个点,最小割变 ...