面试: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()
创建的,我们不妨看看源码里面到底做了什么。
![](https://upload-images.jianshu.io/upload_images/3994917-418b3012f38d386f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
我们不得不看看 Looper
的构造方法都做了什么。
![](https://upload-images.jianshu.io/upload_images/3994917-07c78a5c4449fdb2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/578)
显而易见,确实在创建了 Looper
对象的时候,自动创建了消息队列对象 MessageQueue
。
而 Looper.prepareMainLooper()
从名称也很容易看出来,是直接在主线程内创建对象了。而在我们日常开发中,经常都是在主线程使用 Handler
,所以导致了很少用到 Looper.prepare()
方法。
而生成 Looper
和 MessageQueue
对象后,则自动进入消息循环:Looper.loop()
,我们不妨再看看里面到底做了什么?
![](https://upload-images.jianshu.io/upload_images/3994917-ee0674d26fcc2426.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
截图中的代码比较简单,大家应该不难看懂,我们再看看如何通过 MessageQueue.next()
来取消息设置阻塞状态的。
![](https://upload-images.jianshu.io/upload_images/3994917-30b8732abfff4a5d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
我们取消息采用了一个无限 for 循环,当没有消息的时候,则把标记位 nextPollTimeOutMillis
设置为 -1,在进行下一次循环的时候,通过 nativePollOnce()
直接让其处于线程阻塞状态。
再看看我们的消息分发是怎么处理的,主要看上面的 msg.target.dispatchMessage(msg)
方法。
![](https://upload-images.jianshu.io/upload_images/3994917-21138d983d9030c3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
原来 msg.target
返回的是一个 Handler
对象,我们直接看看 Handler.dipatchMessage(Message msg)
做了什么。
![](https://upload-images.jianshu.io/upload_images/3994917-1c03b306c684c68e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
总结:
- 在主线程中
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
对象。实际上在代码中也是显而易见的。
![](https://upload-images.jianshu.io/upload_images/3994917-932409db508e19d4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
Handler 的另外一种使用方式
前面主要讲解了 Handler.sendMessage(Message msg)
这种常规使用方式,实际上,我们有时候也会用 Handler.post(Runnable r)
进行处理,我们当然应该看看里面是怎么处理的。
![](https://upload-images.jianshu.io/upload_images/3994917-7e60add047c5335f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
从官方注释可以看到,这会直接将 Runnable
对象加到消息队列中,我们来看看 `getPostMessage(r) 到底做了什么。
![](https://upload-images.jianshu.io/upload_images/3994917-c85040592d2bb941.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
我们上面的分析是对的。在 getPostMessage(Runnable r)
方法中,我们除了通过 Message.obtain()
方法来创建消息对象外,专门把 Runnable
对象赋值给了 callback
,这样才用了上面做消息分发的时候,通过这个标记来判断是用的 post()
还是 sendMessage()
方式。
到底是如何发消息的?
一直在说通过 sendMessage()
方式来发消息,到底这个消息是怎么发送的呢?
![](https://upload-images.jianshu.io/upload_images/3994917-cf02d4a7b6f25ef8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
直接看 sendMessageAtTime()
。
![](https://upload-images.jianshu.io/upload_images/3994917-642225634090409c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
enqueueMessage()
里面做了什么?
![](https://upload-images.jianshu.io/upload_images/3994917-5c0dc0000d020351.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
至此,你大概明白了两种方式的区别了。
写在最后
本次内容可能讲的比较多和乱,还望大家跟着到源码中一步一步分析,最困难的时候,就是提升最大的时候!
转载于:https://www.cnblogs.com/liushilin/p/8667917.html
面试:Handler 的工作原理是怎样的?相关推荐
- Dubbo面试 - dubbo的工作原理
Dubbo面试 - dubbo的工作原理 面试题 说一下的 dubbo 的工作原理?注册中心挂了可以继续通信吗?说说一次 rpc 请求的流程? 面试官心理分析 MQ.ES.Redis.Dubbo,上来 ...
- 面试官:你分析过mybatis工作原理吗?
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 Mybatis工作原理也是面试的一大考点,必须要对其非常清晰,这样 ...
- Android——Handler、Loop、MessageQueue的工作原理
为了更好的理解Handler的工作原理,先介绍一下与Handler一起工作的几个组件: Message:Handler接受和处理的消息对象 Looper:每个线程只能拥有一个Looper.它的loop ...
- Android 为什么要有handler机制?handler机制的原理
为什么要有handler机制? 在Android的UI开发中,我们经常会使用Handler来控制主UI程序的界面变化.有关Handler的作用,我们总结为:与其他线程协同工作,接收其他线程的消息并通过 ...
- 面试官:说一下线程池内部工作原理?
以下文章来源方志朋的博客,回复"666"获面试宝典 作者:清泉 cnblogs.com/qingquanzi/p/8146638.html 随着cpu核数越来越多,不可避免的利用多 ...
- mybatis返回null_面试官:你分析过mybatis工作原理吗?
Mybatis工作原理也是面试的一大考点,必须要对其非常清晰,这样才能怼回去.本文建立在Spring+SpringMVC+Mybatis整合的项目之上. 我将其工作原理分为六个部分: 读取核心配置文件 ...
- Android开发:图文分析 Handler通信机制 的工作原理
前言 在Android开发的多线程应用场景中,Handler机制十分常用 下面,将图文详解 Handler机制 的工作原理 目录 1. 定义 一套 Android 消息传递机制 2. 作用 在多线程的 ...
- 面试:说说 HTTPS 的工作原理?
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | stephanietang.github.i ...
- tcp当主动发出syn_一文读懂TCP四次挥手工作原理及面试常见问题汇总
简述 本文主要介绍TCP四次挥手的工作原理,以及在面试中常见的问题. 字段含义 seq序号:Sequence Number,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行 ...
最新文章
- linux ln(link) 命令详解
- 堆内存与栈内存的区别
- IIS 6.0支持.SHTML
- 《你必须掌握的Entity Framework 6.x与Core 2.0》书籍出版
- PowerShell实战1:Ping_Test
- linux系统限制内存使用率,linux中限制CPU和内存占用率方法
- php 远程图片大小,PHP下载远程图片并保存到本地方法总结
- python并发处理机制_Python并发编程—同步互斥
- xms和xmx为什么要相同_为什么开料机价格相差这么多?秘密在这里,你知道吗?...
- 很多家长学历不高,无法辅导孩子的家庭作业怎么办?
- 安装mysql ubantu_安装mysql到ubuntu
- 系统限制,JDK也没有什么好办法
- linux nvme驱动编译,Linux nvme驱动初探
- CwRsync安装配置
- SpringBoot集成redis的LBS功能
- linux centos用smartctl 打开硬盘写缓存
- Google 出的 Guava 是个什么鬼?
- PC硬件之我见——CPU篇
- Vue 添加时间轴 vue-light-timeline
- 推荐:屡试不爽的面试经(转)
热门文章
- C++学习笔记-----在重载的赋值运算函数中调用拷贝构造函数
- Mysql(11)——group by的用法
- 缺失的第一个正数—leetcode41
- ipc$连接失败的常见原因
- CF-825 G.Tree Queries(DFS)
- php如何防止超发,PHP+redis实现的限制抢购防止商品超发功能详解
- Fraction+mysql_MySQL 数据类型总结
- 记录 之 tensorflow常见的数据预处理操作
- jetson nano 系统镜像制作_参考备份Jetson Nano系统(该程序不完备,制作的image不可用,仅供)...
- 中国商用密码SMS4