Handler 面试题及详细真实答案(无书面套话、贴合职场)_第1页
Handler 面试题及详细真实答案(无书面套话、贴合职场)_第2页
Handler 面试题及详细真实答案(无书面套话、贴合职场)_第3页
Handler 面试题及详细真实答案(无书面套话、贴合职场)_第4页
Handler 面试题及详细真实答案(无书面套话、贴合职场)_第5页
已阅读5页,还剩2页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Handler面试题及详细真实答案(无书面套话、贴合职场)一、基础认知类(初级必问)1、简单说说Android中Handler的作用?参考答案:Android主线程不能做耗时操作,子线程不能更新UI,这是系统的线程安全机制。Handler核心就是用来线程间通信,主要作用是把子线程的任务切换到主线程执行,用来刷新UI、更新页面状态。同时它可以配合Message、Runnable,实现任务延时执行、定时循环执行、任务有序排队执行的效果,是Android消息机制的核心工具。2、Handler、Message、MessageQueue、Looper四者的关系?参考答案:这四者是绑定协作的关系,缺一不可:1.Message:消息载体,用来存数据、存任务、标记what标识,是要被传递的内容;2.MessageQueue:消息队列,底层是单链表结构,用来存放所有发送过来的Message,按照入队顺序排队,不会重复、有序执行;3.Looper:消息轮询器,是死循环机制,不断从MessageQueue里取消息,取出后分发给对应的Handler;4.Handler:消息发送者和处理者,负责发送消息、投递任务,同时接收Looper分发的消息,在对应线程处理逻辑。简单总结:Handler发消息→入队MessageQueue→Looper循环取出→Handler处理。3、为什么子线程不能更新UI?参考答案:根本原因是UI控件不是线程安全的。如果允许多个线程同时操作UI,会出现控件状态错乱、界面闪烁、参数异常等问题。系统为了规避这个问题,做了单线程UI模型:只允许主线程操作更新UI。并且ViewRootImpl在触发绘制、刷新的时候,会检查当前线程,非主线程直接抛出异常,这就是子线程更新UI崩溃的原因。4、主线程的Looper什么时候创建的?可以退出吗?参考答案:主线程的Looper是在App启动时,ActivityThread的main方法中自动初始化的,同时会创建全局的MessageQueue,并且开启死循环。主线程Looper不允许退出。如果主动调用quit方法退出Looper,消息循环终止,主线程无法处理页面刷新、点击事件、生命周期回调,App会直接卡死、闪退。子线程的Looper是可以正常quit退出的,用完可以手动释放,避免内存泄漏。二、原理机制类(中级高频)5、Handler发送消息的底层流程?参考答案:1.调用handler.sendMessage()/postRunnable(),最终都会走到sendMessageAtTime方法;2.方法内部会获取当前Handler绑定的Looper和MessageQueue,把Message封装好,加入队列;3.MessageQueue按照执行时间排序,延时消息会排在对应位置,即时消息直接队尾入队;4.Looper死循环next()方法不断阻塞取消息,有消息就取出,无消息就阻塞等待;5.取出消息后,通过msg.target.dispatchMessage()分发回调;6.最终回调Handler的handleMessage方法,或者执行post的Runnable任务。6、Looper的死循环为什么不会卡死ANR?参考答案:很多人误区以为死循环会阻塞页面,其实完全不会。1.Looper循环本身没有耗时操作,只是不断取消息、分发消息;2.MessageQueue没有消息的时候,next()方法会阻塞休眠,释放CPU资源,不会一直占用线程;3.ANR的本质不是Looper循环,是主线程处理单条消息耗时过长,比如消息里做了网络请求、文件解析、大量循环等耗时逻辑,导致后续消息无法及时处理,触发超时。简单说:Looper循环是正常机制,卡顿ANR是开发者写的代码耗时导致的。7、Message为什么推荐obtain()而不是newMessage()?参考答案:系统维护了一个Message消息池,是复用缓存机制。1.obtain()会先从消息池里取闲置的Message对象,复用已有对象,减少频繁创建销毁对象,降低GC压力,性能更好;2.newMessage()每次都会新建对象,频繁发送大量消息,会产生大量临时对象、频繁触发GC,容易造成界面卡顿;3.消息执行完后会自动回收到消息池,等待下次复用,这是系统的性能优化机制,开发中必须统一用obtain。8、Handlerpost()和sendMessage()区别?参考答案:底层本质一致,post只是sendMessage的封装:1.post(Runnable):无需手动定义what、arg参数,直接写任务代码,使用更简单,适合简单UI更新;系统会自动把Runnable封装到Message中;2.sendMessage(Message):需要手动创建、配置Message,支持what、obj、arg1、arg2等参数,适合需要区分消息类型、携带数据的复杂场景。两者最终都会进入同一个消息队列,执行机制、线程切换效果完全一样。三、内存泄漏专项(面试重中之重)9、Handler为什么会造成内存泄漏?原理是什么?参考答案:核心原因是消息持有Activity引用,导致页面销毁无法被回收。1.日常开发中Handler大多写在Activity/Fragment中,默认是匿名内部类、非静态内部类;2.非静态内部类会隐式持有外部Activity的引用;3.Handler发送的延时消息、排队消息会长期存在于MessageQueue中,消息持有Handler引用;4.当Activity关闭销毁时,如果消息还没执行完毕,引用链不会断开:Message→Handler→Activity;5.系统无法回收Activity对象,造成严重内存泄漏,页面越多、延时任务越多,泄漏越严重。10、怎么解决Handler内存泄漏?三种主流方案参考答案:项目中实际用的三种方案,优先级从高到低:方案一:使用静态Handler+弱引用(最常用、面试首选)把Handler定义为static静态类,静态类不会持有外部Activity引用;再通过WeakReference弱引用持有页面实例,弱引用对象在GC时会被主动回收,彻底切断泄漏链。方案二:页面销毁时清空所有消息在Activity/Fragment的onDestroy方法中,调用handler.removeCallbacksAndMessages(null),清空队列所有未执行的任务和消息,杜绝残留消息持有引用。方案三:使用系统自带的Handler(主线程Handler)直接用Handler(Looper.getMainLooper()),不自定义内部Handler,减少引用持有风险,适合简单场景。11、removeCallbacksAndMessages(null)作用是什么?参考答案:这个方法会清空当前Handler在队列中所有未执行的消息和Runnable任务。参数传null代表匹配所有任务,不管延时与否、不管what是多少,全部移除作废。页面销毁时调用,可以防止残留任务继续执行,避免内存泄漏,同时防止页面销毁后还执行UI刷新逻辑,引发空指针崩溃。四、进阶场景与故障排查(高级面试)12、延时消息不准确是什么原因?怎么解决?参考答案:原因:Handler延时是相对延时,不是绝对准时。如果消息队列前面有大量耗时消息、阻塞任务,延时消息需要排队等待,前面任务执行完才会执行,就会出现延迟、不准时。另外手机系统休眠、后台限制、省电策略,也会导致延时任务被延迟、挂起。解决方案:1.避免主线程消息队列堆积耗时任务,减少阻塞;2.高精度定时场景不用Handler,改用AlarmManager、WorkManager;3.业务层做时间校准,任务执行时判断真实时间,修正误差。13、子线程创建Handler需要注意什么?会有什么坑?参考答案:主线程默认初始化Looper,子线程需要手动初始化Looper,否则直接newHandler会直接崩溃。正确流程:1.子线程中先调用Looper.prepare(),创建当前线程的Looper和MessageQueue;2.创建Handler对象,绑定当前线程Looper;3.调用Looper.loop()开启消息循环;4.任务执行完毕后,手动调用Looper.myLooper().quit()退出循环,释放线程资源,避免内存泄漏。常见坑:忘记prepare、忘记loop、用完不quit导致线程常驻内存。14、多个Handler共用一个Looper会有问题吗?参考答案:完全没问题,这是正常机制。一个线程只有一个Looper、一个MessageQueue,可以绑定多个Handler。所有Handler发送的消息都会进入同一个队列排队,Looper统一分发。分发时会通过msg.target区分是哪个Handler发送的,回调对应Handler的处理方法,互不干扰,项目中普遍存在这种场景。15、MessageQueue空闲时的机制是什么?IdleHandler了解吗?参考答案:IdleHandler是系统提供的消息队列空闲回调。当MessageQueue没有待执行消息、主线程空闲的时候,会主动触发IdleHandler的回调。适用场景:可以做一些非即时、不影响页面渲染的轻量任务,比如页面渲染完成后预加载数据、统计上报、缓存清理、初始化次要资源,不抢占主线程绘制时机,优化页面启动速度。注意:回调执行完可返回false只执行一次,返回true会持续监听空闲状态。五、手写代码面试题(实操必考)16、手写安全无泄漏的Handler代码(静态Handler+弱引用)参考答案:PlainText

publicclassMainActivityextendsAppCompatActivity{

//静态Handler+弱引用杜绝内存泄漏

privatestaticclassSafeHandlerextendsHandler{

privatefinalWeakReference<MainActivity>mWeakRef;

publicSafeHandler(MainActivityactivity){

super(Looper.getMainLooper());

mWeakRef=newWeakReference<>(activity);

}

@Override

publicvoidhandleMessage(@NonNullMessagemsg){

super.handleMessage(msg);

MainActivityactivity=mWeakRef.get();

if(activity==null||activity.isFinishing()){

//页面已销毁,不执行逻辑

return;

}

//处理对应业务逻辑

switch(msg.what){

case1001:

//UI更新操作

break;

}

}

}

privatefinalSafeHandlermHandler=newSafeHandler(this);

@Override

protectedvoidonDestroy(){

super.onDestroy();

//清空所有任务,防止残留执行

mHandler.removeCallbacksAndMessages(null);

}

}17、子线程完整使用Handler代码示例参考答案:PlainText

newThread(()->{

//1.初始化Looper

Looper.prepare();

Handl

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论