聊 Android 面试这件事,我踩过的坑大概比很多人想象得要多。前几年我自己跳槽那阵子,简历投出去石沉大海,复盘时才发现问题根本不在"题背得够不够多",而在我讲不清楚一件很朴素的事——这个方案当初为什么选它。后来我陆续帮几个学弟学妹做过模拟面试,去年秋天,一个准备转 Android 方向的学妹拿到了心仪的大厂 offer,把她三个多月整理的复习笔记丢给我看,我才意识到:真正有效的 Android 面试准备,是一套"能力映射"的工程,而不是一份题目清单。
这份笔记加上我自己这些年做技术面试官的经验,基本覆盖了一个 Android 岗位从简历筛选到终面的大部分考察面。不管你是刚学完 Android Studio 环境搭建、能跑通一个增删改查的 App,还是已经写了两三年业务代码想更进一步,下面这些内容都能直接拿去对照补漏。我不会给你一份"必背一百题",而是按"面试官到底在听什么"这个角度,把每一块该讲的深度都拆开。
1. 先别急着背题:Android 面试真正筛的是什么能力
1.1 面试官手里那张看不见的评分表
绝大多数人准备面试的方式是打开一个题库文档,从第一题开始往下刷。这个方式不是没用,但效率极低,因为它假设了"问题"和"能力"是一一对应的。实际上面试官手里通常是一张相对模糊的评分表,大致分成四栏:基础扎实度、工程判断力、表达与沟通、成长潜力。同一道"说说 Handler 机制"的题,不同的人答出来,落点可能完全不同——有人背出了 MessageQueue 和 Looper,有人能讲清楚主线程为什么阻塞在 native 层的 epoll 上却不触发 ANR,还有人能顺带说出自己在项目里怎么用 IdleHandler 做延迟初始化。这三个答案在评分表上的位置是递增的。
我后来跟那位学妹聊她面试中的实际感受,她说最有用的一个转变,是把"我要答对这道题"改成"我要让面试官相信我能独立负责一个模块"。这个转变听起来虚,但落地方式很具体:每讲一个知识点,尽量带上三个东西——它解决什么问题、它内部大致怎么实现、我在真实项目里用它踩过什么坑。前两个靠读,第三个只能靠做和复盘。如果你没有足够的项目经历去支撑第三点,那就用"我读源码时发现的"或者"我在一个小 demo 里验证过的"来替代,至少证明你是动手验证过的人,而不是复述文档的人。
1.2 三类问题的时间配比与准备顺序
我把 Android 岗位的面试问题粗分成三类,它们对应完全不同的准备方式,混在一起准备是最容易浪费时间的。
| 问题类型 | 典型形式 | 考察重点 | 建议投入精力 |
|---|---|---|---|
| 基础原理类 | Java/Kotlin 语法、Handler、Binder、View 绘制 | 概念是否成体系,有没有自相矛盾 | 40% |
| 工程实践类 | 性能优化、崩溃排查、架构选型 | 有没有量化意识和取舍能力 | 40% |
| 场景与手撕 | 算法题、设计题、现场写代码 | 编码基本功、思维清晰度 | 20% |
很多人把 80% 的时间砸在第一类上,结果被问到"你那个启动优化到底省了多少毫秒、怎么测的"就哑了。反过来说,工程实践类是最能拉开差距的地方,因为它很难临时背出来。我的建议顺序是:先用一两周把基础原理的骨架搭起来,然后立刻转到自己项目里找两三个真实问题深挖,最后临考前两周集中刷手撕题保持手感。
1.3 我见过的两种典型错误准备方式
第一种是只背结论不留推导。比如你背下"Android 的 GC 是并发复制收集器",面试官追问一句"那它和 HotSpot 的分代收集有什么区别,为什么要这么设计",你就卡住了。结论是可以查到的,推导过程才能证明你理解。
第二种是过度追求广度。有人会去翻一大堆冷门 API,觉得覆盖面广就稳。但面试官通常只在他自己熟悉的领域深挖,你把十个方向都讲得浮在表面,不如把三四个方向讲到能画图、能写代码、能说出边界条件。我个人的经验是:准备二十个能讲五分钟的话题,比准备两百个能讲三十秒的话题要有效得多。
2. Java 与 Kotlin:从"能用"讲到"为什么这么设计"
2.1 Android 上的运行时和标准 JVM 不是一回事
这是被低估的一个话题。你如果只说"Java 靠 JVM 跑,有垃圾回收",那基本等于没说。Android 从 4.4 之后逐步切到 ART,运行方式在 AOT 和 JIT 之间做过好几轮调整,理解这条演进线,很多性能问题就自然解释得通了。
最早 Dalvik 是解释执行加 JIT,安装快、运行慢,所以早期 Android 手机装个 App 要等很久的"优化中"其实是在做 dexopt。ART 上来之后改成安装时全量 AOT,运行快但安装慢、占空间大。再往后引入 profile 引导的混合编译:先解释执行,把热点方法记录下来,设备空闲时后台编译这些热点,这样就兼顾了安装速度和运行效率。你如果能把这几个阶段和"为什么要这么改"讲清楚,面试官对你的评价会立刻不一样。
GC 也是同理。早期是标记清除加标记整理,容易产生碎片和长时间停顿;后来换成并发复制的方案,把内存分成多个区域并行回收,停顿时间明显缩短;再后面引入分代思路,针对"大部分对象朝生夕死"这个特点做优化。这背后其实就是一句话:移动端的 GC 目标不是吞吐量,而是尽可能短的卡顿。你把这句话讲出来,比背一堆参数有用。
2.2 线程池参数到底怎么算
线程池几乎是必问题,但多数人停在"核心线程数、最大线程数、队列、拒绝策略"这四个名词上。面试官真正想听的是:你凭什么定这组参数。
先说参数含义。任务进来时,如果当前线程数小于核心线程数,直接开新线程;否则丢进阻塞队列;队列满了再尝试把线程数扩到最大值;还是满了就走拒绝策略。这里面最关键、也最容易出问题的是队列的选择。用无界队列的话,最大线程数这个参数基本是废的,因为队列永远不