这段时间集中复盘了一遍Android Framework方向的面试高频点把之前零零碎碎看的源码笔记重新串了一遍。不夸张地说Framework面试翻来覆去就是围着Binder、Handler、启动流程这几座大山打转大多数追问都是从这三条主线发散出去的。这篇是系列笔记的第一篇重点梳理这三块的底层原理、面试回答思路以及那些容易一紧张就答错的关键细节。不管你是刚准备冲击中高级岗位还是已经在面试路上被问蒙过几次这份笔记都适合拿来对照自检。阅读之前建议你先想一个问题面对“说说Binder一次拷贝的原理”你脑子里浮现的是完整的调用链路还是一句背下来的概念如果是后者那这篇笔记正好帮你把断层补上。1. Framework面试到底在考什么在展开具体技术点之前先把范围圈定。很多人一提Framework就头大因为这块东西太庞大了从应用层的ActivityThread一路往下到系统服务、Binder驱动、Linux内核随便问深一点就能把人问倒。但实际面试中面试官并不会真的从内核源码开始拷打你。以我自己的面试经历和身边同行的反馈来看Framework方向的面试题高度集中核心只有三块第一块是跨进程通信IPC也就是Binder。这是整个Android系统的“血管”几乎所有跨进程行为都建立在它之上。面试官喜欢问Binder为什么高效、一次拷贝怎么回事、ServiceManager扮演什么角色。第二块是消息机制也就是Handler、Looper、MessageQueue这套东西。它支撑着主线程的运转方式也是线程切换的基础设施。面试官喜欢往深处问比如postDelay怎么实现、主线程死循环为什么不会卡死ANR、同步屏障是干嘛的。第三块是组件启动与系统服务围绕AMS、Activity启动流程、进程孵化、系统开机流程这些展开。这一块考察的是你对“一个App从点击图标到界面出现中间发生了什么”的整体认知。这三块内容相互咬合Binder是底层通信管道AMS是系统服务中枢ActivityThread是应用进程的入口而Handler又是所有跨进程回调回到主线程以后执行的基础。把这三条线串起来Framework面试的骨架就立住了。另外值得说的是近几年的面试风格越来越不喜欢“背八股”。面试官经常给一个场景让你推理例如“如果启动一个已经存在的singleTask Activity生命周期怎么走”“如果Binder传输的数据超过1MB会怎样”。这些题目没有固定答案拼的就是你对链路理解得透不透。这也是我写这份笔记的初衷不只记结论把因果链和边界条件一起记住。2. Binder全体系最硬的一块骨头2.1 为什么Android非要自己搞一套通信每次面试聊Binder第一个问题大概率是“为什么不用Linux现有的IPC方案”。这个问题看起来简单但能答得系统的人不多。Linux传统的IPC方案包括管道、消息队列、共享内存和Socket。Android之所以不直接用核心原因是三点性能、安全、稳定性。先说性能。传统的管道和Socket通信数据需要从发送方的用户空间拷贝到内核空间再从内核空间拷贝到接收方的用户空间也就是两次拷贝。而Binder利用mmap内存映射只需要一次拷贝数据从发送方用户空间进入内核缓冲区后接收方可以直接映射访问同一块物理内存省掉了第二次拷贝。在移动设备这种对性能敏感的场景里一次拷贝的优势非常明显。再说安全。Binder驱动在传输数据时能拿到调用方的UID和PID内核态直接做身份校验并且每个进程都有独立的Binder节点权限控制。相比之下共享内存虽然性能极好但缺乏安全校验和生命周期管理两个进程一旦约定好内存地址就可以直接读写系统层面很难做细粒度管控。Socket则是基于文件描述符的通信能做身份绑定但语义太重性能也一般。最后说稳定性。Binder的调用模型是“客户端调用服务端方法”语义清晰自带返回值和错误码开发体验接近本地方法调用。对比Socket那套收发字节流、自己解析协议的做法Binder在系统级服务调用场景下显然更适合。所以结论并不是“Linux IPC不行”而是在Android这种“多进程、多系统服务、需要细粒度安全管控”的架构下Binder的综合性价比最高。面试时把这个推理过程讲出来比直接背“性能好、安全”要有说服力得多。2.2 一次拷贝背后到底发生了什么一次拷贝是Binder面试的核心考点但很多人的理解浮于表面。要真正讲清楚得把Binder通信的物理过程拆开。Binder通信里有几个固定角色客户端进程、服务端进程、Binder驱动以及负责服务注册和查询的ServiceManager。当一个服务进程启动时会向Binder驱动注册一个Binder节点这个节点是全局唯一的驱动会为它分配一个句柄。客户端要先通过ServiceManager拿到服务端的句柄之后才能发起调用。调用发生时数据怎么走我画过很多次这个链路最简洁的理解方式是下面这样客户端调用某个接口方法这个方法的实现最终会进入Binder驱动的write操作。Binder驱动在内核空间分配一块缓冲区把客户端用户空间的请求数据拷贝进来。Binder驱动根据目标Binder节点找到服务端进程并通知服务端进程执行方法。关键点来了服务端进程在创建Binder实体的时候通过mmap将一块内核缓冲区映射到了自己的用户空间。也就是说内核里的那块数据缓冲区服务进程可以直接读到不需要再来一次用户态拷贝。服务端执行完方法后把结果数据通过同样的方式回传给客户端。传统IPC需要两次拷贝一次是发送方用户态到内核态一次是内核态到接收方用户态。Binder把第二次拷贝省掉了因为接收方在打开Binder设备时已经通过mmap把内核缓冲区直接映射到了自己的地址空间内核和接收进程共享同一块物理内存。这里有个容易混淆的点mmap是接收方进程在初始化Binder通信时预先完成的映射而不是每次传输都重新映射。一次真正发生的用户态数据拷贝就是客户端向内核缓冲区写入的那一次。面试时如果能说出这一层细节基本就能证明你不是死记硬背。2.3 把Binder的通信流程讲得让面试官点头讲完原理还得会讲流程。很多人在面试时一画Binder架构图就把方向画反客户端和服务端标反了或者漏掉ServiceManager这一步。这里给出一条可以直接照着复述的链路客户端有一个接口代理对象调用代理对象的方法时实际发送的是一个Parcel数据包。这个数据包带着方法ID、参数数据和目标Binder节点标识通过Binder驱动转发到目标进程。目标进程收到请求后把数据包交给对应的Binder实体也就是Stub类Stub解析方法ID并调用真正实现的方法然后把返回值封装成Parcel原路返回。关于ServiceManager你需要知道的是它本身也是一个Binder服务但它的句柄比较特殊固定是0。所有Binder服务在启动时都要向ServiceManager注册把自己的服务名和Binder节点关联起来客户端要调用某个服务时先按服务名去ServiceManager查询拿到句柄后才能建立通信。面试里经常还会追问几个延伸点Binder线程池是做什么的服务端进程维护一个Binder线程池用来并发处理客户端发来的请求。线程池的默认最大线程数有限制所以在高并发调用系统服务时线程池耗尽会让调用阻塞。什么是oneway调用oneway调用意味着客户端发送请求后不等待服务端返回直接返回继续执行常用于不需要返回值、只通知性质的调用例如某些系统回调。Binder传输大小限制是多少Binder内核缓冲区默认限制在1MB以内超过这个限制的传输会失败所以大文件传输不能直接塞进Binder。这些延伸点算是Binder模块的加分题。面试时如果能把主链路讲完、再主动说出两三个延伸细节这个模块基本就稳了。3. Handler消息机制死循环与延时执行的真相3.1 Handler、Looper、MessageQueue三者的分工Handler模块是Android应用层开发者接触最多的Framework机制几乎所有线程切换都依赖它。面试考查的重点不是怎么用而是它内部的几个类怎么协作。一个线程要变成“消息循环线程”必须有一个Looper。Looper内部持有一个MessageQueueMessageQueue里放着按执行时间排序的Message。Handler的作用是往MessageQueue里塞消息以及处理从MessageQueue里取出来的消息。这三个类的关系可以这么记Looper是发动机MessageQueue是传送带Handler是两端操作的机械臂。这里有个隐藏考点多个Handler可以在同一个线程上工作吗可以因为Looper是线程独有的多个Handler共用同一个Looper和MessageQueue它们通过Message的target字段区分自己该处理哪条消息。还有个更隐蔽的考点每个Message创建时可以指定Handler也可以通过Handler.obtainMessage从全局消息池复用Message对象避免反复创建对象带来的内存抖动。线程独有这个概念是用ThreadLocal实现的。Looper在每一个线程里只存一份Looper.myLooper()就是从ThreadLocal里取当前线程的Looper。面试官问到“为什么子线程默认不能用Handler”答案就是子线程没有调用Looper.prepare()ThreadLocal里没有Looper实例消息循环压根没转起来。3.2 主线程死循环为什么不会导致ANR这是Handler模块最经典的一道送命题。很多人一听到“主线程是一个死循环”就慌了觉得这玩意怎么可能不卡。其实这个问题的答案分两层。第一层主线程的Looper确实是一个死循环但它在没有消息的时候会进入休眠状态。Looper.loop()方法的循环体里在取不到下一个消息时会调用MessageQueue的next()方法而next()方法最终会调到nativePollOnce这个Native方法让线程在epoll上等待。epoll是Linux下的一套I/O多路复用机制它可以让线程在等待事件时挂起而不是空转消耗CPU。也就是说没有消息的时候主线程是被挂起来的CPU不会因为它空转而变热。第二层ANR和这个死循环不是一回事。ANR的全称是Application Not Responding它的触发机制是系统在发生特定事件输入事件分发、BroadcastReceiver执行、Service执行等时设置一个超时检测。如果事件在规定时间内没有处理完成系统就判定ANR。注意这个“没有处理完成”意味着有两种可能一种是主线程根本没在执行循环比如被某个同步锁卡住了另一种是主线程正在执行但在做耗时操作导致后续事件没机会处理。而Looper.loop这个死循环本身是一直在跑的它是事件处理的调度器不是阻塞ANR的原因。面试时有个很讨巧的回答方式主线程的Looper死循环就像机场的行李转盘没有行李时传送带也在转但其实是在等待新的行李放上来ANR更像旅客等行李等到超时报警了但传送带本身没坏。这个类比通俗易懂面试官能立刻捕捉到你是否真的理解。3.3 postDelay的排队与唤醒逻辑postDelay是Handler模块的高频追问点。面试官通常会问Handler.postDelay是不是开启了一个定时器是不是单独起了一个线程去睡两次都不是。MessageQueue里的消息是按照触发时间从小到大排序的并不是先进先出。postDelay一条消息后这条消息会被按when时间插入到队列中的合适位置。Looper取消息时会先看队列头部的消息时间如果还没到触发时间就计算还需要等多久把这个时间作为超时参数传给nativePollOnce让线程休眠指定时长。这就是一个超级精妙的点它不是一会儿醒来检查一次而是一次性睡到需要的时刻通过epoll的超时唤醒机制精确控制。新的消息插入后如果新消息的触发时间早于当前正在等待的头部消息MessageQueue会立即唤醒正在阻塞的nativePollOnce重新计算等待时间。举一个场景主线程当前没有消息阻塞在epoll上等待。这时你postDelay了一个10秒后的消息线程会睡10秒再醒来执行。但在第5秒又插入了一条立即执行的消息插入动作会先唤醒主线程执行新消息然后再继续去睡剩下的5秒等待延时消息。这个机制保证了消息处理的及时性不会因为一次长延时阻塞后面插入的紧急消息。3.4 同步屏障与IdleHandler这类加分细节大多数面试者聊到Handler就止步在postDelay但如果你能主动说出同步屏障和IdleHandler面试官会明显觉得你的源码功底不浅。同步屏障的出现是为了解决一类问题某些消息应该优先执行但不能打断当前正在处理的消息。Android的消息体系里Message分同步消息和异步消息两种。正常情况下普通消息都是同步消息。当MessageQueue插入一个同步屏障时next()方法会跳过所有同步消息只执行异步消息直到同步屏障被移除。这套机制在UI渲染里用得最多。系统在Choreographer里注册VSYNC回调时为了保证UI绘制任务不被其他消息阻塞会往MessageQueue里插入一个同步屏障把绘制相关的异步消息优先执行。绘制完成后再移除同步屏障。面试官追问“同步屏障什么时候用”答出“UI渲染帧回调优先处理”就算过关。IdleHandler则是另一个方向的考点。它不是一个Handler而是MessageQueue里的一个回调接口。当Looper在消息队列为空或者没有到触发时间的空闲时段会执行IdleHandler里注册的任务。常见的用法包括启动优化时的延迟加载、页面空闲时的预加载等。需要小心的是IdleHandler里如果再往队列里插入新消息会影响当前空闲状态的处理逻辑所以不要在空闲回调里做一些不必要的高频操作。4. 四大组件启动一条链路串起AMS与进程4.1 冷启动Activity的完整调用链Activity启动流程是Framework面试的必考题而且这题分值很高、区分度也很大。最基础的要求是能画出跨进程调用链路进阶要求是能说清楚哪一步在哪一个进程里发生。以冷启动为例也就是从桌面点击App图标开始。整个过程可以压缩成下面这条链路Launcher.startActivity - Instrumentation.execStartActivity - ActivityTaskManager.getService().startActivity - AMS.startActivity - ActivityStarter.execute - 判断进程与任务栈若无进程则请求Zygote孵化 - Zygote fork出新进程运行ActivityThread.main - ActivityThread.attach(ApplicationThread) - AMS.attachApplication - AMS通过ApplicationThread请求创建Application和Activity - ActivityThread.handleLaunchActivity - Activity.onCreate - onStart - onResume这条链路里前半段发生在Launcher进程中间一段跨越到AMS所在的system_server进程再往后又跨到新孵化的应用进程。面试时能指出“这段是从Launcher进程发起”“这段发生在system_server”“这段才回到App进程”就已经赢了八成的选手。几个值得记住的类ActivityTaskManager是Android 10之后从AMS中拆出来的专门负责Activity任务栈和应用栈管理ActivityStarter是AMS内部负责解析Intent、检查启动标志、决定复用还是新建Activity的组件ApplicationThread是App进程向AMS提供回调的Binder服务端接口AMS通过它来指挥App进程执行创建Application、创建Activity、执行生命周期等操作。ActivityThread是App进程的入口类它的main方法就是应用进程的Java入口主线程消息循环也在这里启动。App进程所有的组件实例都在ActivityThread这个环境里被管理所以常有人说ActivityThread不是Thread而是管理线程的类这个说法是对的。4.2 进程与线程在启动过程中分别扮演什么角色启动流程里最容易混淆的是进程和线程的分工。很多人以为启动一个App是“开了一个新线程”其实不是。App启动是“开了一个新进程”进程是独立的内存空间和资源单元。Zygote通过fork系统调用复制出新的进程这个新进程里会初始化Binder线程池并运行主线程。ActivityThread.main是这个新进程主线程的入口主线程负责创建Application、创建Activity、运行主线程消息循环。子线程是后来按需创建的。所以准确的说法是一次冷启动包含一次进程创建如果目标Activity所在进程还没启动以及一次主线程消息循环的建立。同一个进程里的多个Activity之间不需要跨进程通信而不同进程里的组件比如两个不同进程的Activity之间的调度就需要AMS跨进程协调。这里有个经典面试延伸题A进程通过Intent启动B进程的ActivityB进程还没启动那么AMS是先创建B进程还是先创建Activity答案是先创建B进程然后在B进程环境里再创建Activity。因为Activity是运行在进程里的没有进程就没有Activity的容器。这个顺序在日常开发里感知不到但在进程管理、启动优化这块是核心逻辑。4.3 launchMode、任务栈与onNewIntent的关联记忆Activity的启动模式也是高频考点而且和启动流程深度绑定。NoHistory和singleTask这种组合题更是面试官的心头好。单靠背结论很容易翻车但如果你从任务栈的角度理解会顺畅很多。standard模式是每次都新建Activity放进启动它的那个栈singleTop是栈顶复用栈顶如果已经是该Activity就不新建而是调用onNewIntentsingleTask是任务栈内唯一实例栈里没有就新建有就把这个Activity上方的所有Activity弹出singleInstance则是单独占一个任务栈。面试官特别喜欢追问onNewIntent的场景题比如当前栈顶是A再打开一个singleTop的A此时走onNewIntent还是onCreate答会走onNewIntent因为栈顶复用了。当前栈是A-B-C此时打开一个singleTask的B栈里B上方有C会发生什么答C会被弹出B被复用并走到onNewIntent此时栈变成A-B。如果栈里根本不存在B呢答会直接new B放到栈顶。这里的隐藏细节是onNewIntent不是生命周期方法它是Activity被复用而不会重新创建时用来传递新Intent的回调。在复用场景下onCreate是不走的所以数据更新逻辑必须放在onNewIntent里处理否则就白传了。这个细节实际开发中踩坑率极其高。还有一些进阶组合例如singleTask和taskAffinity配合使用单Activity多任务栈这会涉及到Task和TaskAffinity的概念。建议备考时把任务栈的栈帧结构画一遍比盯着文字结论效率高得多。5. 系统启动从开机到SystemServer的服务编排5.1 一条清晰的启动时间线系统启动流程是Framework面试中相对独立的一块面试官偶尔会抽出来考察你对全局架构的认知。完整的启动链路可以列成下面的时间线BootROM引导 - Bootloader - Linux内核 - init进程用户空间的第一进程 - init.rc解析并启动Zygote - Zygote预热并启动SystemServer - SystemServer启动各类系统服务AMS、PMS、WMS等 - 启动SystemUI和Launcher这里有个容易混淆的点Zygote和SystemServer都是由init进程启动的但顺序很关键。Zygote先启动因为SystemServer本身就是Zygote fork出来的第一个进程。SystemServer里承载着几乎所有的Java系统服务包括ActivityManagerService、PackageManagerService、WindowManagerService等。面试官问到“系统第一个进程是谁”要分两层回答在内核态第一个进程是init在Java层第一个进程是Zygote。SystemServer是Zygote的第一个子进程。这个区分能体现你对进程维度的掌握程度。5.2 Zygote为什么在体系里这么重要Zygote是Android系统的“受精卵”它孵化了所有的应用进程包括SystemServer。Zygote存在的最大意义是加速应用启动。原理是这样的Zygote在启动时先加载并初始化一批常用的类和资源包括核心Java类库、常用系统类、主题资源、WebView的某些壳资源等。当需要创建新应用进程时Zygote直接调用fork系统调用复制自己新进程带着父进程已经加载好的类和资源直接运行不需要再从零加载。fork的过程是内核级的进程复制速度远快于“创建一个新进程然后重新加载所有运行环境”。这也是为什么Android的应用进程启动看起来比桌面平台轻快很多。面试时如果能补充一点fork出来的子进程只有调用线程存活其他线程都不存在因为fork只会复制调用线程。Zygote里如果有其他线程比如JIT编译线程等在fork后的子进程里都不会保留。这个点不算难但容易让面试官眼前一亮。5.3 一个高频冷门追问Zygote为什么用socket而不用Binder这个问题我在不止一场面试里遇到属于典型的“看着冷门实际上很考架构认知”的问题。先澄清背景App进程的孵化请求最终是由AMS发给Zygote的。AMS通过一个名为“zygote”的socket和Zygote通信具体方式是AMS把自己要启动的类名、进程名、UID等打包成参数通过socket发送给ZygoteZygote接收后准备fork并执行特定类。那为什么不用Binder常见解释有几层第一层是启动顺序的问题。Zygote是在系统极早期启动的那会儿Binder驱动和ServiceManager可能都还没完全准备好。Binder通信需要Binder驱动初始化、需要打开binder设备、需要建立Binder线程池这套依赖链在早期环境中可能形成循环依赖。而socket只需要一对文件描述符依赖极其简单非常适合早期引导阶段。第二层是基于fork行为的设计考量。Zygote要fork出子进程fork的本质是复制父进程的地址空间、文件描述符表等。如果Zygote自己持有大量的Binder线程资源和连接状态fork时这些东西也会被复制到子进程里子进程刚出生就继承了一堆乱七八糟的Binder状态容易引发线程和句柄的冲突。socket相对简单fd复制后只要按需使用就行。第三层是职责单一。Zygote的孵化请求本身很简单传参、fork、设置进程名这几个动作用socket就够了犯不上引入一整套Binder通信体系。面试时我能给出的最佳回答策略是先见识广度从启动顺序和依赖关系切入再从深度补充提到fork对线程和fd的继承问题。这两层答出来这道冷门题基本就转化成加分题了。6. 高频问答速查表与踩坑经验6.1 面试前快速过一遍的问答清单把上面讲过的所有内容压缩成一个自测清单面试前过一遍非常管用。这里按我自己的面试经历整理的常问问题问题核心回答为什么Android不用传统IPC而用Binder性能上省一次拷贝安全上内核态校验UID/PID语义上接近本地方法调用Binder一次拷贝是怎么实现的客户端一次拷贝到内核缓冲服务端通过mmap共享内存直接读取无需二次拷入用户空间ServiceManager和Binder驱动的关系ServiceManager是句柄为0的特殊Binder服务负责服务注册与查询主线程Looper死循环为什么不卡没有消息时epoll挂起线程不消耗CPUANR是事件处理超时与死循环本身无关postDelay是怎么实现的消息按时间排序头部消息的剩余等待时间作为epoll超时时间新插入更早消息会立即唤醒同步屏障是什么target为null的特殊消息插入后只执行异步消息用于UI绘制优先场景冷启动Activity全链路Launcher - AMS - Zygote fork - ActivityThread - ApplicationThread - handleLaunchActivityZygote为什么用socket早期依赖简单避免fork复制Binder线程状态职责单一ApplicationThread起什么作用是App进程暴露给AMS的Binder回调接口AMS通过它管理App进程生命周期onNewIntent什么时机走Activity复用且不重建时singleTop栈顶复用、singleTask复用调用这张表不是让你背答案而是检测你看到问题后脑子里能不能立刻浮现出对应的因果链。如果只有答案没有因果面试官随便追问一层就露馅了。6.2 回答时的常见雷区面试时踩过的坑值得拿出来单独说。第一个雷区是和面试官争执“Binder到底拷贝了几次”。Binder的“一次拷贝”是相对于传统IPC说的但严格来说客户端到内核缓冲的那次拷贝是实实在在发生的。如果你只说“拷贝一次”而不解释清楚是哪一次很容易被追问到当场卡壳。建议回答方式是先承认用户态到内核态的一次拷贝是必然的再说mmap让接收方不需要第二次拷贝。第二个雷区是混淆进程和线程的启动关系。很多人把“冷启动创建进程”和“主线程运行”混为一谈。严格来说Zygote fork出来的新进程只有一个线程也就是主线程主线程里执行ActivityThread.main。后续其他线程才是在进程初始化过程中创建的。面试时把这个层次讲清楚能避开很大一部分追问。第三个雷区是画图方向搞反。画Binder架构图时最容易犯的错误是客户端和服务端位置搞反或者ServiceManager画在数据通路的中间位置。实际上ServiceManager只负责查询并不在客户端和服务端每次调用的路径上。画图时一定要标明客户端拿到句柄后接下来的通信链路是客户端 - Binder驱动 - 服务端不再经过ServiceManager。第四个雷区是只答结论不推因果。比如问到“为什么不用共享内存”如果只答“不安全”面试官会觉得没有深度。至少要补一层共享内存缺少内核态的权限校验两个进程只要掌握内存地址就能通信系统无法介入。这个“为什么”比结论本身值钱。6.3 一些独家记忆妙招这些是我自己备考时用的记忆技巧分享出来供参考。Binder的链路可以用一句话记客户端发Parcel驱动做转发服务端读Parcel返回再走一遍。只要记住Parcel是Binder通信里唯一的“通用语言”很多细节比如方法ID、参数类型序列化、容错处理都围绕它展开。Handler的机制可以用“排序队列 挂起等待 按点唤醒”三个词记住。消息队列是时间有序集合没有消息时通过epoll挂起新消息插入时判断是否需要立即唤醒。启动流程可以用“外到内”的方式记忆Launcher在桌面AMS在系统服务端ActivityThread在App进程。面试时把三个进程区分清楚调用链自然就能顺下来。系统启动只需要记一条主轴线init - Zygote - SystemServer - 各种服务 - Launcher。每次提到系统启动相关的细节都先回到这条轴线确认顺序基本不会错。7. 备考路线源码怎么读才有效率这部分放在最后因为看完前面的笔记你可能会想这些东西是不是真的要一行行读源码才能掌握我的经验是不用甚至不建议逐行啃但要会抓关键链路。我备考时的顺序是先精读Handler因为它最简单、最容易验证随便写个Demo都能感受到效果而且它是理解主线程运转的基础。然后读Binder重点不是驱动代码而是Binder的Java层架构和应用层IPC调用链。最后读启动流程把ActivityThread的main方法、AMS的startActivity方法串起来过一遍。读数源码之前先把自己当个“架构侦探”。拿到一个类的源码不要从头看到尾先找入口方法再找它调用的关键方法把调用关系一层层抽出来。举个例子看ActivityThread时不需要读完整个文件只需要知道main - attach - handleLaunchActivity这条主干其他的细节后续需要时再查。用实际场景验证知识是我最推荐的方法。比如写一个自定义子线程设置Looper的代码验证一下它的消息循环机制再写一个singleTop的Demo观察onNewIntent和onCreate的打印顺序。代码量很小但能把纸面上的知识固化成真正属于自己的理解。还有一个很实用的工具断点调试。在Handler.postDelay、MessageQueue.next、ActivityThread.handleLaunchActivity、AMS.startActivity这几处打断点跑一次应用启动和消息发送配合调用栈看比读十篇源码分析文章都有效。我备考时做过的实验清单包括观察postDelay后next方法的等待状态、观察跨进程启动Activity时的调用链线程名、观察同步屏障在渲染期间的存在等。最后说一句我对Framework面试的体会这个方向面来面去表面上考的是知识点实际考的是有没有建立系统模型的习惯。源码是有限的但基于链路推演新场景的能力是无限的。把Handler、Binder、启动流程这三条主线吃透以后再去看WMS、PMS、ContentProvider等扩展点会明显发现学习成本大幅降低因为你的认知地图已经有了骨架。这篇笔记先到这下一篇打算把WMS窗口管理、PMS包管理、以及Binder驱动的更多底层细节梳理出来。