2023金三银四常见Handler面试总结,附带答案

news2024/11/24 17:23:39

以下的Handler的面试题都是在面试过程中总结出来比较常见的面试题,现在分享给大家,希望可以帮助你们!

1.Handler的实现原理

从四个方面看Handler、Message、MessageQueue 和 Looper Handler:负责消息的发送和处理 Message:消息对象,类似于链表的一个结点; MessageQueue:消息队列,用于存放消息对象的数据结构; Looper:消息队列的处理者(用于轮询消息队列的消息对象) Handler发送消息时调用MessageQueue的enqueueMessage插入一条信息到MessageQueue,Looper不断轮询调用MeaasgaQueue的next方法 如果发现message就调用handler的dispatchMessage,dispatchMessage被成功调用,接着调用handlerMessage()。

2.子线程中能不能直接new一个Handler,为什么主线程可以

主线程的Looper第一次调用loop方法,什么时候,哪个类 不能,因为Handler 的构造方法中,会通过Looper.myLooper()获取looper对象,如果为空,则抛出异常, 主线程则因为已在入口处ActivityThread的main方法中通过Looper.prepareMainLooper()获取到这个对象, 并通过 Looper.loop()开启循环,在子线程中若要使用handler,可先通过Loop.prepare获取到looper对象,并使用Looper.loop()开启循环

3.Handler导致的内存泄露原因及其解决方案

原因:1.Java中非静态内部类和匿名内部类都会隐式持有当前类的外部引用 2.我们在Activity中使用非静态内部类初始化了一个Handler,此Handler就会持有当前Activity的引用。 3.我们想要一个对象被回收,那么前提它不被任何其它对象持有引用,所以当我们Activity页面关闭之后,存在引用关系:

"未被处理 / 正处理的消息 -> Handler实例 -> 外部类",如果在Handler消息队列 还有未处理的消息 / 正在处理消息时 导致Activity不会被回收,从而造成内存泄漏

解决方案: 1.将Handler的子类设置成 静态内部类,使用WeakReference弱引用持有Activity实例 2.当外部类结束生命周期时,清空Handler内消息队列

4.一个线程可以有几个Handler,几个Looper,几个MessageQueue对象

一个线程可以有多个Handler,只有一个Looper对象,只有一个MessageQueue对象。Looper.prepare()函数中知道,。在Looper的prepare方法中创建了Looper对象,并放入到ThreadLocal中,并通过ThreadLocal来获取looper的对象, ThreadLocal的内部维护了一个ThreadLocalMap类,ThreadLocalMap是以当前thread做为key的,因此可以得知,一个线程最多只能有一个Looper对象, 在Looper的构造方法中创建了MessageQueue对象,并赋值给mQueue字段。因为Looper对象只有一个,那么Messagequeue对象肯定只有一个。

5.Message对象创建的方式有哪些 & 区别

Message.obtain()怎么维护消息池的 1.Message msg = new Message(); 每次需要Message对象的时候都创建一个新的对象,每次都要去堆内存开辟对象存储空间 2.Message msg = Message.obtain(); obtainMessage能避免重复Message创建对象。它先判断消息池是不是为空,如果非空的话就从消息池表头的Message取走,再把表头指向next。 如果消息池为空的话说明还没有Message被放进去,那么就new出来一个Message对象。

消息池使用Message 链表结构实现,消息池默认最大值 50。消息在loop中被handler分发消费之后会执行回收的操作,将该消息内部数据清空并添加到消息链表的表头。 3.Message msg = handler.obtainMessage(); 其内部也是调用的obtain()方法

6.Handler 有哪些发送消息的方法

sendMessage(Message msg) sendMessageDelayed(Message msg, long uptimeMillis) post(Runnable r)

postDelayed(Runnable r, long uptimeMillis) sendMessageAtTime(Message msg,long when)

7.Handler的post与sendMessage的区别和应用场景

1.源码

sendMessage sendMessage-sendMessageAtTime-enqueueMessage。

post

sendMessage-getPostMessage-sendMessageAtTime-enqueueMessage getPostMessage会先生成一个Messgae,并且把runnable赋值给message的callback

2.Looper->dispatchMessage处理时

public void dispatchMessage(@NonNull Message msg) { if (msg.callback != null) { handleCallback(msg); }else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }dispatchMessage方法中直接执行post中的runnable方法。

而sendMessage中如果mCallback不为null就会调用mCallback.handleMessage(msg)方法,如果handler内的

callback不为空,执行mCallback.handleMessage(msg)这个处理消息并判断返回是否为true,如果返回true,消息

处理结束,如果返回false,handleMessage(msg)处理。否则会直接调用handleMessage方法。

post方法和handleMessage方法的不同在于,区别就是调用post方法的消息是在post传递的Runnable对象的run方法中处理,而调用sendMessage方法需要重写handleMessage方法或者给handler设置callback,在callback的handleMessage中处理并返回true

应用场景

post一般用于单个场景 比如单一的倒计时弹框功能 sendMessage的回调需要去实现handleMessage Message则做为参数 用于多判断条件的场景。

8.handler postDealy后消息队列有什么变化,假设先 postDelay 10s, 再

postDelay 1s, 怎么处理这2条消息sendMessageDelayedsendMessageAtTime-sendMessage

postDelayed传入的时间,会和当前的时间SystemClock.uptimeMillis()做加和,而不是单纯的只是用延时时间。

延时消息会和当前消息队列里的消息头的执行时间做对比,如果比头的时间靠前,则会做为新的消息头,不然则会从消息头开始向后遍历,找到合适的位置插入延时消息。 postDelay()一个10秒钟的Runnable A、消息进队,MessageQueue调用nativePollOnce()阻塞,Looper阻塞;

紧接着post()一个Runnable B、消息进队,判断现在A时间还没到、正在阻塞,把B插入消息队列的头部(A的前面),然后调用nativeWake()方法唤醒线程;

MessageQueue.next()方法被唤醒后,重新开始读取消息链表,第一个消息B无延时,直接返回给Looper; Looper处理完这个消息再次调用next()方法,MessageQueue继续读取消息链表,第二个消息A还没到时间,计算一下剩余时间(假如还剩9秒)继续调用nativePollOnce()阻塞;

直到阻塞时间到或者下一次有Message进队;

9.MessageQueue是什么数据结构

内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表 这点和传统的队列有点不一样,主要区别在于Android的这个队列中的消息是按照时间先后顺序来存储的,时间较早的消息,越靠近队头。 当然,我们也可以理解成,它是先进先出的,只是这里的先依据的不是谁先入队,而是消息待发送的时间

10.Handler怎么做到的一个线程对应一个Looper,如何保证只有一个

MessageQueue ThreadLocal在Handler机制中的作用

设计的初衷是为了解决多线程编程中的资源共享问题, synchronized采取的是“以时间换空间”的策略,本质上是对关键资源上锁,让大家排队操作。 而ThreadLocal采取的是“以空间换时间”的思路, 它一个线程内部的数据存储类,通过它可以在制定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据, 对于其他线程就获取不到数据,可以保证本线程任何时间操纵的都是同一个对象。比如对于Handler,它要获取当前线程的Looper,很显然Looper的作用域就是线程,并且不同线程具有不同的Looper。 ThreadLocal本质是操作线程中ThreadLocalMap来实现本地线程变量的存储的 ThreadLocalMap是采用数组的方式来存储数据,其中key(弱引用)指向当前ThreadLocal对象,value为设的值 通过ThreadLocal计算出Hash key,通过这个哈 ThreadLocal对象,value为设的值

11.HandlerThread是什么 & 好处 &原理 & 使用场景

HHandlerThread本质上是一个线程类,它继承了Thread; HandlerThread有自己的内部Looper对象,通过Looper.loop()进行looper循环;

通过获取HandlerThread的looper对象传递给Handler对象,然后在handleMessage()方法中执行异步任务;

优势: 1.将loop运行在子线程中处理,减轻了主线程的压力,使主线程更流畅,有自己的消息队列,不会干扰UI线程 2.串行执行,开启一个线程起到多个线程的作用

劣势: 1.由于每一个任务队列逐步执行,一旦队列耗时过长,消息延时 2.对于IO等操作,线程等待,不能并发我们可以使用HandlerThread处理本地IO读写操作(数据库,文件),因为本地IO操作大多数的耗时属于毫秒级别,对于单线程 + 异步队列的形式 不会产生较大的阻塞

12.IdleHandler及其使用场景

Handler 机制提供的一种,可以在 Looper 事件循环的过程中,当出现空闲的时候,允许我们执行任务的一种机制. IdleHandler在looper里面的message处理完了的时候去调用 怎么使用 IdleHandler 被定义在 MessageQueue中,它是一个接口. 定义时需要实现其 queueIdle() 方法。返回值为 true 表示是一个持久的 IdleHandler 会重复使用,返回 false 表示是一个一次性的IdleHandler。

IdleHandler 被 MessageQueue 管理,对应的提供了addIdleHandler() 和 removeIdleHandler() 方法。将其存入 mIdleHandle addIdleHandler() 和removeIdleHandler() 方法。将其存入 mIdleHandlers 这个 ArrayList 中。 什么时候掉用 就在MessageQueue的next方法里面。

MessageQueue 为空,没有 Message; MessageQueue 中最近待处理的 Message,是一个延迟消息(when>currentTime),需要滞后执行;

使用场景

1.Activity启动优化:onCreate,onStart,onResume中耗时较短但非必要的代码可以放到IdleHandler中执行,减少启动时间

2.想要在一个View绘制完成之后添加其他依赖于这个View的View,当然这个用View#post()也能实现,区别就是前者会在消息队列空闲时执行 优化页面的启动,较复杂的view填充 填充里面的数据界面view绘制之前的话,就会出现以上的效果了,view先是白的,再出现. app的进程其实是ActivityThread,performResumeActivity先回调onResume , 之后 执行view绘制的measure, layout, draw,也就是说onResume的方法是在绘制之前,在onResume中做一些耗时操作都会影响启动时间 把在onResume以及其之前的调用的但非必须的事件(如某些界面View的绘制)挪出来找一个时机(即绘制完成以后)去调用即可。

13.消息屏障,同步屏障机制what

同步屏障只在Looper死循环获取待处理消息时才会起作用,也就是说同步屏障在MessageQueue.next函数中发挥着作用。

在next()方法中,有一个屏障的概念(message.target ==null为屏障消息), 遇到target为null的Message,说明是同步屏障,循环遍历找出一条异步消息,然后处理。 在同步屏障没移除前,只会处理异步消息,处理完所有的异步消息后,就会处于堵塞 当出现屏障的时候,会滤过同步消息,而是直接获取其中的异步消息并返回, 就是这样来实现「异步消息优先执行」的功能

how

1、Handler构造方法中传入async参数,设置为true,使用此Handler添加的Message都是异步的; 2、创建Message对象时,直接调用setAsynchronous(true) 3.removeSyncBarrier() 移除同步屏障:应用在 View 更新时,draw、requestLayout、invalidate 等很多地方都调用了ViewRootImpl#scheduleTraversals(Android应用框架中为了更快的响应UI刷新事件在ViewRootImpl.scheduleTraversals中使用了同步屏障

14.子线程能不能更新UI

刷新UI,

都会调用到ViewRootImpl.Android每次刷新UI的时候,最终根布局ViewRootImpl.checkThread()来检验线程是否是View的创建线程。 ViewRootImpl创建的第一个地方,从Acitivity声明周期handleResumeActivity会被优先调用到,也就是说在OnResume后ViewRootImpl就被创建,这个时候无法在在子线程中访问UI了,上面子线程延迟了一会handleResumeActivity已经被调用了,所以发生了崩溃 不延迟在creae里直接设置不会崩溃 线程更新UI也行,但是只能更新自己创建的View

15.为什么Android系统不建议子线程访问UI

在android中子线程可以有好多个,但是如果每个线程都可以对ui进行访问,我们的界面可能就会变得混乱不堪,这样多个线程操作同一资源就会造成线程安全问题,当然,需要解决线程安全问题的时候,我们第一想到的可能就是加锁,但是加锁会降低运行效率,所以android出于性能的考虑,并没有使用加锁来进行ui操作的控制。

16.Android中为什么主线程不会因为Looper.loop()里的死循环卡死?MessageQueue#next 在没有消息的时候会阻塞,如何恢复?

他不阻塞的原因是epoll机制,他是linux里面的,在native层会有一个读取端和一个写入端,当有消息发送过来的时候会去唤醒读取端,然后进行消息发送与处理,没消息的时候是处于休眠状态,所以他不会阻塞他。

17.Handler消息机制中,一个looper是如何区分多个Handler的当

Activity有多个Handler的时候,怎么样区分当前消息由哪个Handler处理处

理message的时候怎么知道是去哪个callback处理的

每个Handler会被添加到 Message 的target字段上面,Looper 通过调用 Message.target.handleMessage() 来让 Handler 处理消息。

18.Looper.quit/quitSafely的区别

当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空, 无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。

当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。

19.通过Handler如何实现线程的切换

当在A线程中创建handler的时候,同时创建了MessageQueue与Looper,Looper在A线程中调用loop进入一个无限的for循环从MessageQueue中取消息,当B线程调用handler发送一个message的时候,会通过msg.target.dispatchMessage(msg);将message插入到handler对应的MessageQueue中,Looper发现有message插入到MessageQueue中,便取出message执行相应的逻辑,因为Looper.loop()是在A线程中启动的,所以则回到了A线程,达到了从B线程切换到A线程的目的。

20.Handler 如何与 Looper 关联的

通过构造方法 mLooper = Looper.myLooper()->sThreadLocal.get()( sThreadLocal.set)

21.Looper 如何与 Thread 关联的

Looper 与 Thread 之间是通过 ThreadLocal 关联的,这个可以看 Looper#prepare() 方法 Looper 中有一个ThreadLocal 类型的 sThreadLocal静态字段,Looper通过它的 get 和 set 方法来赋值和取值。 由于 ThreadLocal是与线程绑定的,所以我们只要把 Looper 与 ThreadLocal 绑定了,那 Looper 和 Thread 也就关联上了

22.Looper.loop()源码

for无限循环,阻塞于消息队列的next方法 取出消息后调用msg.target.dispatchMessage(msg)进行消息分发

23.MessageQueue的enqueueMessage()方法如何进行线程同步的

就是单链表的插入操作 如果消息队列被阻塞回调用nativeWake去唤醒。 用synchronized代码块去进行同步。

24.MessageQueue的next()方法内部原理

next() 是如何处理一般消息的?

next() 是如何处理同步屏障的?

next() 是如何处理延迟消息的?

调用 MessageQueue.next() 方法的时候会调用 Native 层的 nativePollOnce() 方法进行精准时间的阻塞。在Native 层,将进入 pullInner() 方法,使用 epoll_wait 阻塞等待以读取管道的通知。如果没有从 Native 层得到消息,那么这个方法就不会返回。此时主线程会释放 CPU 资源进入休眠状态。

25.子线程中是否可以用MainLooper去创建Handler,Looper和Handler

是否一定处于一个线程

可以的。 子线程中Handler handler = new Handler(Looper.getMainLooper());,此时两者就不在一个线程中

26.ANR和Handler的联系

Handler是线程间通讯的机制,Android中,网络访问、文件处理等耗时操作必须放到子线程中去执行,否则将会造成ANR异常。 ANR异常:Application Not Response 应用程序无响应 产生ANR异常的原因:在主线程执行了耗时操作,对Activity来说,主线程阻塞5秒将造成ANR异常,对BroadcastReceiver来说,主线程阻塞10秒将会造成ANR异常。 解决ANR异常的方法:耗时操作都在子线程中去执行 但是,Android不允许在子线程去修改UI,可我们又有在子线程去修改UI的需求,因此需要借助Handler。

最近整理很多Android面试题,一共包含了25个知识点,答案也已经整理好了,但是每个知识点问题都不多,不过都是最近两年比较常见的,有需要的可以点击这里或者点击文末小卡片获取!。

知识点:

  • Java基础

  • Android基础

  • Android中高级

  • Activity

  • Service

  • BroadcastReceiver

  • ContentProvider

  • Handler

  • View绘制

  • View事件分发

  • Binder

  • 性能优化

  • BroadcastReceiver

  • Viewpager&Fragment

  • WebView

  • 动画

  • Bitmap

  • Ams

  • APP系统启动流程

  • 序列化及Art

  • 模块化及组件化

  • 热修复

  • AOP

  • jetpack

  • 开源框架

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/401624.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FL Studio21最新中文版下载及切换语言教程

随着近年来摇滚、电音的发展,越来越多的人开始对电子音乐编曲感兴趣,而电音编曲的首要条件,就是需要一个好的DAW(数字音频工作站),常用的DAW有很多,例如Cubase、Nuendo、Pro Tools、 SONAR等等&…

Unity - 搬砖日志 - Texture.mipmapBias 无效的解决方法

文章目录环境原因解決方案Referenes环境 Unity : 2020.3.37f1 Pipeline : BRP 原因 因为美术发现有些贴图太糊,但是经过研究发现,mipmap0就是完全够精度的 但是不可能还要提升贴图的尺寸,因为经过多方咨询(咨询TA大佬&#xff0…

2023 年会是网络安全的关键年吗?

过去 12 个月对网络安全领域和周围的每个人来说再次充满挑战。和往年不同,感觉很不一样,攻击源源不断。过去,大型漏洞每季度发生一次,但在过去一年中,在某些情况下,我们几乎每周都会处理严重漏洞。 已知利…

itop-3568开发板驱动学习笔记(7)高级字符设备(一)阻塞 IO 和 非阻塞 IO

《【北京迅为】itop-3568开发板驱动开发指南.pdf》 学习笔记 文章目录阻塞 IO非阻塞 IOIO 实验(使用等待队列)等待队列阻塞 IO 实验非阻塞 IO 实验阻塞 IO I/O输入/输出(Input/Output),分为IO设备和IO接口两个部分。 在POSIX兼容的系统上&…

python学习——【第四弹】

前言 上一篇文章 python学习——【第三弹】 中学习了python中的流程控制语句,这篇文章我们接着学习python中的序列。先给大家介绍不可变序列 字符串和可变序列 列表,下一篇文章接着补充元组,集合和字典。 序列 指的是一块可以存放多个值的…

多线程的风险 --- 线程安全

✨个人主页:bit me👇 ✨当前专栏:Java EE初阶👇 ✨每日一语:低头赶路,敬事如仪;自知自心,其路则明。 目 录🍸一. 线程不安全🍹二. 线程不安全的原因&#x1f…

【C语言】每日刷题 —— 牛客语法篇(1)

前言 大家好,今天带来一篇新的专栏c_牛客,不出意外的话每天更新十道题,难度也是从易到难,自己复习的同时也希望能帮助到大家,题目答案会根据我所学到的知识提供最优解。 🏡个人主页:悲伤的猪大…

Java的jar包打包成exe应用

将springboot项目使用maven打出的jar包,打成windows平台下exe应用程序包(自带jre环境)。 工具:1、exe4j 2、Inno Setup 工具放到网盘,链接:https://pan.baidu.com/s/1ZHX8P7u-7GBxaC6uaIC8Ag 提取码&#x…

VMware16pro虚拟机安装全过程

很多时候需要用到Linux系统,简单的一种方式可以是:Windows系统运行Linux(Windows Subsystem for Linux)不过有些时候还是需要虚拟机来运行Linux,也更方便点,比如在做嵌入式系统的烧录等操作都需要Linux环境…

人的高级认知:位置感

你知道吗?人有个高级认知:位置感 位置感是啥?咋提高位置感? 趣讲大白话:知道自己几斤几两 【趣讲信息科技99期】 ******************************* 位置感 就是对自己所处环境和自身存在的领悟 属于人生智慧 来源于阅历…

设计模式——创建型模型——单列模式(8种实现)

前言: 👏作者简介:我是笑霸final,一名热爱技术的在校学生。 📝个人主页:个人主页1 || 笑霸final的主页2 📕系列专栏:计算机基础专栏 📧如果文章知识点有错误的地方&#…

保姆级使用PyTorch训练与评估自己的EfficientNetV2网络教程

文章目录前言0. 环境搭建&快速开始1. 数据集制作1.1 标签文件制作1.2 数据集划分1.3 数据集信息文件制作2. 修改参数文件3. 训练4. 评估5. 其他教程前言 项目地址:https://github.com/Fafa-DL/Awesome-Backbones 操作教程:https://www.bilibili.co…

eps文件删除了能恢复吗?恢复误删eps文件的三种方法

eps文件格式专为矢量图像和图形而设计。虽然没有被广泛使用,但它仍然受到各种插画家和平面设计师的钟爱。eps文件十分适合创建徽标和商标设计,主要应用见于广告牌、海报和横幅。可是在使用设备过程中,难免会遇到数据丢失问题,如果…

KaiwuDB 时序引擎数据存储内存对齐技术解读

一、理论1、什么是内存对齐现代计算机中内存空间都是按照 byte 划分的,在计算机中访问一个变量需要访问它的内存地址,从理论上看,似乎对任何类型的变量的访问都可以从任何地址开始。但在实际情况中,通常在特定的内存地址才能访问特…

并发编程---java锁

java锁一 多线程锁synchronized案例分析1.1synchronized介绍1.2 synchronized案例分析1.2.1.标准访问,请问先打印邮件还是短信?1.2.2.邮件⽅法暂停4秒钟,请问先打印邮件还是短信?分析1.2.3.新增⼀个普通⽅法hello(&…

Django系统开发

Django系统开发 1.新建项目 创建Django项目 删除templates目录 删除settings.py里templates -> DIRS的列表数据 2.创建app 在Pycharm中 注册app 在settings.py中找到 INSTALLED_APPS 加入对应路径 app01.apps.App01Config 3.表结构 from django.db import modelsclas…

32位处理器AM6528BACDXEA、AM6548BACDXEAF基于Arm Cortex-A53内核【工业4.0嵌入式产品应用】

AM6528BACDXEA、AM6548BACDXEAF 处理器是专为满足工业4.0嵌入式产品对处理性能的复杂需求而设计的Arm应用处理器。AM654x和AM652x器件将四个或两个Arm Cortex-A53内核与一个双Arm Cortex-R5F MCU子系统结合在一起。这些包含的功能旨在帮助客户实现终端产品的功能安全目标。它还…

绘制CSP的patterns矩阵图

最近在使用FBCSP处理数据,然后就想着看看处理后的样子,用地形图的形式表现出来,但是没有符合自己需求的函数可以实现,就自己尝试的实现了一下,这里记录一下,方便以后查阅。 绘制CSP的patterns矩阵图 对数据做了FBCSP处理,但是想画一下CSP计算出来的patterns的地形图,并…

成功的项目管理策略:减少成本,提高质量

项目管理是一项具有挑战性的任务,项目团队需要合理的规划和策略,以确保项目的成功和达成预期。为了实现项目的成功,项目经理必须采用正确的策略,才能以最大限度地减少成本并提高项目质量。本文将探讨成功的项目管理策略&#xff0…

Django实践-05Cookie和Session

文章目录Django实践-05Cookie和SessionDjango实践-05Cookie和Session用户登录的准备工作1. 创建用户模型。2. 正向工程生成数据库表3.写utils.py文件,密码转md54.给数据表tb_user中插入测试数据5.编写用户登录的视图函数和模板页。6.编写urls.py。6.增加login.html模…