十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

2022 Android面试八股文:Handler、Binder与AMS核心机制解析

2022 Android面试八股文:Handler、Binder与AMS核心机制解析 从2022年5月中旬开始我一边在职一边准备Android面试白天写业务晚上刷八股文。那段时间我把网上能翻到的Android面试题、大厂面经、源码解析笔记几乎都过了一遍最后沉淀出一份自己的答题套路。现在整理出来说说我在准备Android面试八股文这件事上到底在看什么、背什么、怎么答。先说一个很多人没想明白的问题八股文为什么值得认真准备因为面试官问你的很多题目表面是考背书能力实际是考你有没有真正理解这套系统是怎么转起来的。Handler、Binder、AMS、事件分发、四大组件、ANR、内存泄漏这些2022年依然是Android面试的硬通货无论你简历上写的是业务开发还是性能优化方向第一轮技术面大概率就是围绕这些东西展开。你连消息机制都讲不清楚后面聊项目、聊架构、聊协程面试官很难信服你的基础扎实。我这篇八股文篇不是把网上所有题都抄一遍而是按我自己复习时的思路重新拆了一遍哪些是必背的哪些是面试官大概率会追着往下问的哪些是答完一句原理之后还能展开加分的内容。如果你离面试还有一两周建议先按后面的节奏来。1. 为什么2022年7月我还在啃八股文环境判断与复习节奏1.1 这个时间节点面试风向2022年年中的Android面试和两年前相比明显变了几个方向。第一Kotlin已经完全是主流Co我们写新项目基本全Kotlin面试时候Java经常只被用来问基础但协程、Flow、Compose这些新潮内容才是加分点。第二大厂对源码级理解的要求更高了光会说Handler用于线程间通信已经不够面试官会追问Looper在子线程怎么创建、MessageQueue怎么阻塞、为什么不会卡死主线程。第三项目经历之外的软技能和思考深度被提到了很高的权重。所以八股文不再只是死记硬背而是要把知识点串成一条完整的链路。比如问Activity启动流程你能不能从Launcher的startActivity一直讲到AMS、zygote、ApplicationThread、ActivityThread这整条线。你需要给面试官一种我读过源码我懂系统的调用链的感觉而不是碎片化地背几个名词。1.2 我的复习资料与节奏安排我当时给自己定了两周的冲刺计划资料其实很固定《Android开发艺术探索》和《深入理解Android内核设计思想》翻重点章节尤其是消息机制、View体系、Binder这几块。网上能找到的大厂面经合集只看近半年内的因为有些问题每年会换花样。自己项目里真实踩过的坑每个坑都往源码层面挖一层作为项目里体现基础的素材。刷题不要贪多我每天只过三个大类系统机制类、View体系类、性能优化类。时间分配上我会用周末两天把所有核心知识点做成一个脑图工作日晚上每天攻克一个主题。比如周一看Handler周二看Binder周三看AMS周四看View周五看性能。周六把这一周的问题过一遍周日整理成自己的答题话术。2. Handler、Binder、AMS这三个机制几乎决定了面试的下限2.1 Handler消息机制从主线程Looper到同步屏障面试官问Handler最常见的开场是你讲讲Handler机制。很多人会顺口说主线程有LooperLooper从MessageQueue里取消息Handler调用handleMessage处理。这句话没有错但只能拿到基础分。真正加分的回答应该是先指出Looper是ThreadLocal存储的每个线程只有一个Looper、只有一个MessageQueue。然后说主线程的Looper是在ActivityThread.main方法里通过Looper.prepareMainLooper()创建的它不会退出。接着讲Handler的sendMessage最终会调用enqueueMessage把Message放到MessageQueue插入的时候按when字段做时间排序不是按投递顺序。面试官这时候往往会追一句MessageQueue没消息的时候主线程在干什么这里才是关键。MessageQueue的next方法是阻塞的它调用nativePollOnce进入epoll机制的等待让主线程休眠不占用CPU等有消息进来或者有binder事件的时候通过管道/eventfd唤醒。很多只背理论的人会卡在这一步能把native层这一句讲出来面试官会觉得你真看过源码。还有一个容易被追问的点是同步屏障SyncBarrier。正常Message是一串同步消息有需要时可以往MessageQueue插入一个target为null的Message作为同步屏障这个屏障后面的同步消息就不会被执行只能执行异步消息。View的Choreographer在doFrame时插入VSYNC消息就用了这个机制。2022年这里有个变种问法Android 12/13之后引入了异步消息以及畅行等等面试官会问你有没有细致研究过主线程消息优先级你提一下SyncBarrier就够了。2.2 Binder面试题一次拷贝到底省了什么Binder是Android面试里最容易答飞的一个点。常见问题就是Android为什么用Binder做跨进程通信而不用共享内存、Socket、管道先记住结论性话术Binder基于C/S架构发送方调用一次copy_from_user把数据从用户空间拷贝到内核空间接收方通过mmap映射直接把内核空间那同一份内存映射到自己的用户空间所以是一次拷贝。传统管道、Socket至少需要两次拷贝。然后准备一个类比一次拷贝就像你寄快递快递员先到你家里把包裹拿走放到了中转站收件人直接拿着钥匙开中转站的门把包裹拿走中间不需要再有人把包裹送到收件人手里。而这个中转站就是内核对同一块物理内存的映射。Binder面试还会考几个延伸点Binder线程池是怎么工作的每个进程启动时会创建一个Binder线程池默认上限是16个Binder线程执行handleMessage等事务。Binder驱动在哪个文件核心在kernel层你只要说binder.c 即可。四大组件跨进程通信哪些用了BinderAMS、PMS、WMS都是系统服务Android 10之后一些系统服务改用AIDL。Binder的死亡代理linkToDeath。oneway和非oneway的呼叫区别这个很多人在项目中用过AIDL但没细想。我复习Binder时最大的体会是不要光背一次拷贝要理解mmap为什么能省一次拷贝。因为接收方通过mmap把内核缓冲区直接映射到用户态发送方复制到内核缓冲区的数据接收方不用再复制一遍直接读取即可。2.3 Activity启动流程把AMS拉进回答里Activity启动流程在2022年依然是高频题而且面试官普遍希望你在回答中自然冒出AMS、ActivityTaskManager、Zygote、ApplicationThread这些角色。一个标准的回答骨架是这样Launcher进程调用startActivityActivityManagerShellCommand或者Instrumentation会通过ATMS得到Binder代理。真正执行的是AMS.startActivity再由ActivityTaskManager内部处理任务栈。AMS先检查进程是否存在不存在就通过Socket请求Zygote fork新进程。Zygote fork出的新进程启动ActivityThread调用main方法然后scheduleLaunchActivity回到AMS说我准备好了。最后通过ApplicationThread把LaunchActivity的消息发回主线程ActivityThread的handleLaunchActivity执行真正的生命周期回调。这个流程里有一个很刁钻的追问onCreate、onResume与AMS回调的时序是发生在什么阶段你要是说onCreate在attach之后调用那是对的但你得说清楚AMS只是负责状态管理和调度真正的生命周期执行是由ActivityThread在主线程里完成的。还有一道常考的衍生题一个App冷启动的完整过程涉及了哪些系统服务答案是Zygote、SystemServer、AMS/PMS、ActivityThread、Application还有Launcher。每一个都可以展开说至少一分钟这就是八股文的厉害之处一环套一环。3. View体系问答事件分发与绘制流程的五连问3.1 事件分发三兄弟和U型管道View事件分发是我个人觉得最像考驾照扣分细则的一个知识点细节很多但一旦理解了下发和回溯的U型管道整个逻辑就顺了。先默写三个方法的职责dispatchTouchEvent事件分配入口返回true表示事件被消费。onInterceptTouchEventViewGroup专属是否拦截该事件。onTouchEvent自身是否消费事件。从ACTION_DOWN开始事件从RootView往下传到手指所在的View如果中间某个ViewGroup的onInterceptTouchEvent返回true事件就被它拦下来交给它的onTouchEvent。如果一路没人拦截到了目标View它的onTouchEvent如果返回false事件会沿着父容器一层层回溯直到有人消费。这就是经典的U型管道。面试官喜欢挖的细节有三个同一个事件序列中ACTION_DOWN与ACTION_MOVE的关系如果DOWN没人消费这一整个序列都不会再传过来如果MOVE被拦截子View会收到ACTION_CANCEL。requestDisallowInterceptTouchEvent(CANCEL)写在子View里可以禁止父容器拦截。事件分发和点击事件的onClick怎么串起来onTouchEvent里有个performClick的调用点但只有先消费了DOWN和UP且事件没有被打断才会触发performClick。我建议复习时用一张脑图把dispatchTouchEvent的返回值画出来因为面试时能准确说出DOWN返回trueMOVE继续分发给同一目标如果MOVE返回false但DOWN是true最终会照样消费掉这种细节的人其实是少数。3.2 绘制流程measure、layout、draw的边界和触发条件绘制流程的八股经常和自定义View一起考。面试官会问自定义View的onMeasure为什么要小心或者layout和draw的触发时机是什么我整理的答题顺序是开头先说入口ViewRootImpl.performTraversals里面依次调用performMeasure、performLayout、performDraw。然后说MeasureSpec的三种模式EXACTLY确定尺寸、AT_MOST最大限制、UNSPECIFIED无限制。接着说MeasureSpec的值是由父View的MeasureSpec和自身的LayoutParams通过getChildMeasureSpec计算出来的。最后说layout阶段确定View的位置draw阶段通过Canvas绘制背景、内容、子View、装饰。最容易踩的坑是onMeasure里不遵守SpecMode。比如ListView/RecyclerView的item高度被设为wrap_content时如果不处理AT_MOST模式会造成item复用错乱或者显示异常。实践里我在onMeasure里拿到AT_MOST模式时会计算最小高度保证逻辑合理这个细节可以作为面试答题时的一个亮点。关于invalidate和requestLayout的区别也是必问invalidate会执行draw但不会relayoutrequestLayout会重新measurelayoutdraw一般用于尺寸变化。频繁调requestLayout容易掉帧这也能串到性能优化上。3.3 自定义View衍生题onMeasure的防坑点2022年面试问自定义View已经不满足于你画过一个圆了。面试官会拿出一套组合拳如果View的宽高不指定会显示多大 答案是0。你必须自己在onDraw里处理Drawable的自然宽高或者在measure时让模pact。onDraw里能不能做耗时操作 不能因为主线程绘制超过16ms就会掉帧。为什么RecyclerView滑出屏幕的item会复用 因为ViewHolder机制但如果你用wrap_content且onMeasure有问题复用会出现显示错乱。如果在onDraw里postInvalidate会怎样 会变成每帧都重新绘制性能暴雷。这些衍生题其实考察的还是你对measure和draw的理解是否透彻。所以我复习时把自定义View分成两条线一条线要求自己画一个圆形进度条并说清onMeasure里如何支持wrap_content另一条线要求自己说清requestLayout的调用链也就是ViewRootImpl.performTraverse如何被触发。每一条线都准备2分钟左右的口头讲解面试时再临场演绎。4. 性能优化追问链ANR、内存泄漏与卡顿的底层逻辑4.1 ANR你以为的5秒其实不是全部ANR的八股题标准答案是Input事件5秒无响应、前台广播10秒、后台广播60秒、前台Service 20秒、ContentProvider超时。但这些数值不是重点重点在于ANR是怎么判定的。面试官想听的回答是系统通过消息机制检测超时。比如输入事件InputDispatcher会设置一个超时时间如果事件在队列里长时间没有被处理完就会触发ANR。所以主线程阻塞才是ANR的真正原因。我当时为了准备ANR特意去看了自己项目里MainLooper现场的日志掌握了两个关键信息一是日志会有main looper、input dispatching timed out这些关键词二是要能判断是死锁、任务耗时还是Binder等待。有时候面试官会把ANR和Handler联系起来为什么主线程必须有个Looper因为要不断处理MessageQueue里的待办事件比如输入、布局、绘制三大块。如果某个消息耗时太久后面的所有消息都会排队输入事件超时就被系统判定为ANR。4.2 内存泄漏常见场景和LeakCanary原理内存泄漏的八股大多数人都能说出几个场景静态变量持有Activity、Handler持有Activity、匿名内部类持有外部类、资源没关闭、单例传入Context。但面试官问为什么Handler持有Activity会造成泄漏很多人就开始含糊。正确答法是Handler在子线程发了一个延迟消息MessageQueue持有这个MessageMessage.target指向HandlerHandler又持有外部Activity的引用导致Activity无法被回收。回答LeakCanary怎么检测泄漏时不要只说WeakReference要说出关键机制Activity.onDestroy之后LeakCanary会通过Application.ActivityLifecycleCallbacks拿到这个Activity把它放到一个WeakReference里然后过几秒主动触发一次GC如果WeakReference.get()还是不为null就说明发生了泄漏再通过ReferenceQueue做确认。这里还有一个加分点手动调用System.gc()并不一定立刻GCLeakCanary会等待IdleHandler空闲时再GC多次检测后确认泄漏。这个细节体现你对GC和ART运行时有一定理解。4.3 卡顿监控从Choreographer到FrameCallback卡顿优化题基本会成为你的项目做过什么性能优化的引子。我准备的标准话术是卡顿的本质是掉帧也就是一帧的绘制超过了16.6ms。用Choreographer的FrameCallback可以拿到帧间隔如果两帧之间的时间差大于16ms就说明有卡顿。但真正要定位是哪儿卡还需要用Systrace/Method Tracing抓主线程耗时。2022年有一些更细的考法比如为什么说渲染线程的耗时也算进掉帧 因为Android 5.0之后引入了RenderThreadChoreographer的FrameCallback只管App侧实际发到GPU渲染由RenderThread负责如果GPU忙不过来即使主线程不卡也会丢帧。性能优化的八股里我还准备了一个非常实用的模板先用systrace看有没有binder sync、layout、measure等明显耗时。再看内存抖动频繁创建对象导致GC频繁用Memory Profiler抓Allocation。最后用BlockCanary或自研的Looper.getMainLooper().setMessageLogging哪个Message耗时超过阈值就记录堆栈。这样答出来会显得你有完整的方法论而不只是背了一个掉帧概念。5. Jetpack与架构设计2022年面试的加分区5.1 MVVM 协程标配回答的完整版本2022年Android面试聊架构默认大家都会说MVVM。但要是只答数据驱动UIViewModel持有LiveDataView监听LiveData那还是太单薄。一个七八分的回答应该是这样UI层通过Activity/Fragment持有ViewModelViewModel暴露状态UI使用观察者模式监听状态变化数据的获取放在Repository层远程或本地数据源都通过Repository返回给ViewModelViewModel不持有View的引用所以屏幕旋转时不会因为View重建而重建。再加一点协程可以替代部分LiveData的场景。LiveData是感知生命周期且自动管理但它在多线程和Flow复杂转换上不如Kotlin Flow灵活。所以常见架构是UI层用LiveDataRepository以上用Flow这个表达在2022年大厂面试里非常常见。如果你再能说出ViewModel为什么能在Activity重建后保持存活就是加分完成了因为ViewModelStore在Activity里Activity被销毁重建时非配置变更场景下ViewModelStore会保留只有finish时才会clear。5.2 生命周期组件与ViewModel原理被忽略的细节面试官有时会专门深挖Lifecycle原理问Lifecycle是怎么感知Activity生命周期的答案是在Activity的onCreate里通过ReportFragment.injectIfNeededIn(activity)注入一个无UI的Fragment利用Fragment的生命周期回调把Activity的生命周期事件分发出去。或者在新版本里Activity通过LifecycleRegistry调用handleLifecycleEventObserver就会被唤醒。另一个容易被问到的细节是onSaveInstanceState和ViewModel的区别。onSaveInstanceState适合保存临时性的、可以被序列化的数据但如果是大对象或非序列化对象用ViewModel更合适。而且onSaveInstanceState在进程被系统杀死后还能恢复ViewModel在进程被杀死后不会存活所以两者是互补的。准备这部分时我给自己出的一个验证题是我实现一个自定义LiveData让它在多个观察者不同生命周期下正确分发事件怎么做 想通这个能应对大多数Lifecycle的追问。5.3 该不该提Compose新技术的面试尺度2022年7月问Compose大概率是一个印象题。面试官不指望你多精通但想知道你是否关注了新技术、有没有实践意识。我的建议是不要主动大篇幅说Compose除非你项目里真的用了。更安全的说法是我了解Compose的声明式UI思想也看过它的重组机制但当前项目还是以View体系为主团队还没有引入Compose。然后可以聊Compose的核心优势状态驱动UI、重组只发生在State变化处、用Composable函数描述界面。如果你能补充一两句副作用API的原理比如remember、LaunchedEffect、DisposableEffect的区分就更扎实了。但一定不要生拉硬扯聊到你不熟的地方非常容易露怯。6. 容易漏掉的边角料考点类加载、混淆、adb与网络6.1 Java/Kotlin细节泛型擦除与协程挂起在八股文篇里我把Java/Kotlin基础单独列了一类因为很多人在准备Android题时会忽略语言层的问题但2022年面试官喜欢混合考。Java层最容易考到的是泛型擦除编译成字节码时泛型类型会被擦除运行时拿不到真正的泛型类型。面试官会问我能不能通过反射拿到List 的类型参数答案是不能除非用TypeToken类库来绕开。Kotlin协程这块高频考点是挂起函数的原理。挂起函数本质是状态机编译器会把一个挂起函数编译成一个Continuation对象保存当前状态和局部变量当挂起恢复时再继续执行。我用一个答题金句挂起不是阻塞是把CPU让出去等结果恢复后在原来的栈上继续走而不是新开线程。6.2 R8与APK瘦身混淆规则的高级问法热搜词里出现了android r8和android studio混淆2022年面试也确实会问App优化的相关。先理解R8是干嘛的它是ProGuard的升级替代品负责代码压缩、资源压缩、优化和混淆。通过obfuscate阶段把类名、方法名改短减少包体积。常见的追问是R8和ProGuard有什么区别 答案R8把shrink裁剪、optimize优化、obfuscate混淆、desugar脱糖四步合并成一个统一的编译期工具效率比ProGuardDX/D8高很多。APK瘦身的高频问法包括为什么开启android:extractNativeLibsfalse可以让安装包变小 因为压缩andAlign库可以减少占用空间。图片资源为什么用WebP 同等质量下比PNG体积小。为什么可以配置shrinkResources 因为R8去掉无效代码后关联的未使用资源也可以被裁掉。6.3 adb与常用调试现场排错的能力题不要小看adb命令面试官在问项目时经常顺手考你怎么看一个App的当前Activity怎么查找ANR日志怎么判断内存占用我整理我必背的一组命令adb shell dumpsys activity top查看当前界面Activity。adb shell dumpsys meminfo package查看进程内存占用。adb pull /data/anr/拉取ANR日志。adb shell kill -3 pid主动触发一次ANR日志生成。adb shell am start -n package/activity拉起指定页面。这些命令的回答要结合场景不要背菜单。比如你说自己排查过ANR就可以说先用dumpsys看主线程状态和堆栈然后从/data/anr/traces里找到线程名和锁等待关系。这样面试官会觉得你确实干过而不是只会背命令。7. 八股文怎么答才有区分度从背题到聊体系7.1 面试官问八股的真实意图面试官让你介绍Handler机制他真实的目的是什么绝对不是想看你会不会背一遍源码注释而是想判断你是否理解Android系统的消息循环模型能否应对系统为什么这么设计。遇到性能或异常问题时能否从机制层面推测根因。有没有持续看源码和深入思考的习惯。所以答题的时候不要一口气把源码全背出来要像讲故事一样先说模型再说为什么最后配合场景。要留给面试官追问的空间他说不定会接着你的回答问出更多细节。我在模拟面试时要求自己回答每个核心题都不超过80秒但在这80秒内必须包含一个结论、一个原理、一个例子。比如Handler结论是主线程靠LooperMessageQueue循环处理消息原理是nativePollOnce阻塞、epoll唤醒例子是postDelay不是定时器只是按时间排序插入到时间才从队列取出。这样的回答就比光背流程有区分度。7.2 被追问时的应对不要急着给出所有细节面试时最忌讳的是面试官一开口就把我所有知道的都倒出来结果他没法再往下问。更自然的策略是先给到七分等追问再给到九分。举例面试官问你了解AMS吗你可以先答AMS是系统服务中管理Activity、Service、BroadcastReceiver生命周期和任务栈的核心类它在SystemServer进程里。如果面试官继续问那它怎么和App通信你再展开Binder和ApplicationThread。他如果只问了第一层你就不要直接背到zygote避免显得像在背课文。遇到不会的问题也要有一套话术这块我平时没有从源码层面细看过但是根据我对xxx的理解我觉得大概和xxx有关。如果我有机会深入了解我会xxx。 这句话比硬编胡造强一百倍也比答不上来就沉默强很多。7.3 如果准备时间只剩两周的个人建议最后分享一个适合冲刺的实操方案是我自己用着比较顺的第1~3天把Handler、Binder、AMS这三个最硬核的机制彻底吃透每天拿一台手机或者模拟器一边看源码一边画时序。第4~5天View事件分发和绘制流程用自己写过的小Demo验证。第6~7天性能优化三件套ANR、内存泄漏、卡顿和自己的项目结合。第8~9天Jetpack全家桶重点看ViewModel、LiveData、Lifecycle。第10~11天过一遍基础语言题和网络题刷一些面经。第12~13天模拟面试自己录音回听哪里有停顿、哪里表达不清。第14天查漏补缺把最不熟的三个点再背一遍。每天保证输出别光看。我会把当天复习的题目用我自己讲给自己听的方式说一遍经常录音之后发现真能顺畅讲三分钟以上的知识点才算真的学会了。很多题目隔一天再讲一次如果讲不顺就说明还需要回炉。八股文准备这件事说白了就是系统梳理 刻意输出。你能把每个知识点用正常人听得懂的话讲清楚讲到自己不觉得卡壳面试时大概率也不会出大问题。2022年7月这个时间点Android面试的要求是在变高但底层的那些机制没变把基础盘扎稳了后面聊项目、聊架构都会有底气。
返回列表