面试: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环境. ...
随机推荐
- vxWorks BSP主要文件目录的组成及主要文件的作用
vxWorks BSP主要文件目录的组成及主要文件的作用 1.目录target/config/All: 这个目录下的文件是所有BSP文件共享的,不是特别需要不要更改里面的任何文件. a.configA ...
- RAID10与RAID01比较,RAID10与RAID5比较
RAID10和RAID01的比较RAID10是先做镜象,然后再做条带. RAID01则是先做条带,然后再做镜象. 比如以6个盘为例,RAID10就是先将盘分成3组镜象,然后再对这3个RAID1做 ...
- SecurityError:Error #2048:安全沙箱冲突
1.错误描述 SecurityError:Error #2048:安全沙箱冲突:http://localhost:8080/YHD/flash/YHD.swf 不能从 http://123.89.45 ...
- 校园网IPv6加速
对于广大学生来说,上网是一件很纠结的事情,校园网要么按时间计费,要么按流量计费,要么是校园宽带.按时间计费速度慢,按流量计费费用高,校园宽带还不能共享,只能电脑开热点给手机上网.有没有既能提高网速又经 ...
- 如何使用jQuery-ContextMenu实现右击菜单
最近在做项目中,遇到一个棘手的问题,页面上有很多功能需要实现,每个功能需要绑定一个按钮.如果一个功能绑定一个按钮,那么将会占用页面很大的空间,而且可能会使页面变得不美观.思前想后,决定将所有按钮做成右 ...
- BZOJ 5039: [Jsoi2014]序列维护
5039: [Jsoi2014]序列维护 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 282 Solved: 169[Submit][Status ...
- 【BZOJ1189】紧急疏散(二分答案,最大流)
[BZOJ1189]紧急疏散(二分答案,最大流) 题面 Description 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是 ...
- 【BZOJ1207】【HNOI2004】打鼹鼠(动态规划)
[BZOJ1207][HNOI2004]打鼹鼠 题面 BZOJ题面 题解 考虑到m的范围只有10000 O(m^2)的复杂度是可以接受的 所以直接暴力DP 每次枚举前面出现的鼹鼠 检查是否能够转移过来 ...
- UVA10692:Huge Mods
题面 传送门 题意 输入正整数a1,a2,a3..an和模m,求a1^a2^...^an mod m Sol 首先有\[ a^b\equiv \begin{cases} a^{b\%\phi(p)}~ ...
- Heavy Transportation POJ - 1797
题意 给你n个点,1为起点,n为终点,要求所有1到n所有路径中每条路径上最小值的最最值. 思路 不想打最短路 跑一边最大生成树,再扫一遍1到n的路径,取最小值即可,类似Frogger POJ - 22 ...