京东Android真题复盘:从Java基础到Handler机制与AMS
发布时间:2026/8/31 8:04:18 作者:尧图编辑部 阅读量:1,286

2019年秋招京东Android开发工程师的笔试题考完很多人在讨论一个问题这套题到底刷掉了多少份简历。我的真实感受是题目难度并没有到劝退级别但它把Android开发至少需要掌握的十几年知识体系浓缩到了一张卷子里。Java容器、并发编程、Handler、AMS、进程优先级、网络、性能优化、构建打包几乎是覆盖全图式出题任何一块是短板当场就会露馅。这篇文章不打算只给你罗列“某年真题答案”而是结合我对手头流传版本的复盘把选择题的出题思路、手写题的得分逻辑、面试追问中容易翻车的机制问题以及工程化工具链里的隐藏考点全部过一遍。适合正在准备Android校招、实习面试的同学也适合社招想回炉基础的工程师参考。整篇的落脚点不是“背答案”而是建立一个能稳定应对笔试和面试的知识框架。1. 当年考题的基本盘选择题里的Java基础与Android机制1.1 考点分布的底层规律为什么Java基础占比最高京东2019这套题的选择题部分放在今天看依然有代表性。它没有多少偏门冷知识大部分题目都是“平时天天用但没深想就答不对”的类型。我综合考生回忆整理了一下分布大致是考点方向考察频率典型问法Java基础与集合高HashMap和Hashtable的区别、ArrayList扩容Java并发与多线程高volatile的可见性、线程池参数Android核心机制高Handler消息循环、Activity启动模式内存优化与性能中内存泄漏场景、ANR原因网络与安全中HTTPS握手、混淆规则数据结构与算法选择低到编程题里考察这套分布背后有一个非常实际的逻辑大厂校招的人进来之后是要直接投放到业务团队写代码的。Java基础决定你写的代码稳不稳Android机制决定你能不能定位线上问题集合和并发出题多是因为电商类App的高并发场景天然敏感。所以笔试不是在为难你而是在提前筛掉那些只会调用API、不懂原理的简历。很多同学备考的时候疯狂刷LeetCode结果选择题丢分严重。我的建议是算法题决定你能不能被面试官高看一眼而基础选择题决定你能不能过笔试线两条腿都要走不能偏废。1.2 选择题里的高频考点拆开看背后的原理单选题部分有四类题几乎年年遇到我挑几个典型展开说。第一类HashMap相关。问法通常是JDK8中HashMap在什么条件下由链表转为红黑树为什么这么设计。答案是链表长度达到8且数组长度达到64。但考官更希望你说出两个key8是泊松分布下链表长度超过8的概率极低属于空间和时间的折中64是为了避免数组还很小的时候就盲目转树导致扩容后又要拆树。这类题表面考阈值实际考你对哈希冲突处理和数据结构复杂度权衡的理解。第二类Handler消息机制。出题点集中在一个线程有几个LooperHandler的post和sendMessage有什么区别为什么子线程不能直接更新UI。我印象里有一道变体题问Handler构造时如果没有调用Looper.prepare会抛出什么异常很多人在这个点上栽了。如果你能把ThreadLocal、Looper.loop的死循环、MessageQueue.next的阻塞唤醒机制串起来讲一遍这道题无论怎么变都能答对。第三类Activity启动模式。four种启动模式standard、singleTop、singleTask、singleInstance单纯记区别不难但京东喜欢出带FLAG的变式比如Intent加了FLAG_ACTIVITY_NEW_TASK或者FLAG_ACTIVITY_CLEAR_TOP之后实际启动栈会怎么变化。这里要特别注意flag的优先级高于manifest配置而且singleTask实例复用时会回调onNewIntent不会走onCreate这个细节是面试追问的常客。第四类进程优先级与内存回收。Android系统在内存不足时按什么顺序杀死进程答案是空进程、后台进程、服务进程、可见进程、前台进程依次回收但实际工程里还要加一层同一优先级内占用内存越大越容易被杀。理解这个顺序才能理解为什么前台Service里做耗时任务会被系统盯上为什么WorkManager比自建Service更适合后台任务。2. 手写题想拿稳分关键在于读懂考官意图2.1 单例模式DCL为什么容易写错编程题里出现设计模式几乎是必然的最常考的是单例。如果只是让写一个能用的单例标准答案有很多种但考场上最常见的写法是双重校验锁也就是DCL。问题是很多人写着写着就把volatile丢了。public class Singleton { private static volatile Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }volatile在这里解决的是指令重排问题。instance new Singleton()不是原子操作它分三步分配内存、调用构造方法、把引用赋值给instance。如果没有volatile线程A可能先完成第三步、构造方法还没执行完线程B进入getInstance发现instance不为null直接返回一个半初始化对象程序就会出现匪夷所思的空指针。volatile的禁止重排语义保证了前面的写操作不会被后面的写操作覆盖。考官考这道题不是想看你背模板而是想看你会不会解释每一步。如果你能顺带说出静态内部类实现也会延迟加载且不用同步锁说明你是理解过的如果你还能提一句枚举单例天然防序列化那就是加分项了。2.2 LRU缓存从原理到代码落地数据结构类手写题里LRU缓存出现频率非常高。京东这类业务方很关注“热点数据”的淘汰策略所以LRU不光是LeetCode题更是实际工程里的常见需求。如果允许直接使用Java集合LinkedHashMap能一行核心逻辑搞定public class LRUCacheK, V extends LinkedHashMapK, V { private final int capacity; public LRUCache(int capacity) { super(capacity, 0.75f, true); this.capacity capacity; } Override protected boolean removeEldestEntry(Map.EntryK, V eldest) { return size() capacity; } }构造函数里的第三个参数true表示按访问顺序排序每次get或put都会把节点移到链表尾部头部自然就是最久没被访问的数据。removeEldestEntry在插入后判断是否超过容量超过就移除头部节点这样get和put的复杂度都是O(1)。但如果考官要求你不能用LinkedHashMap就需要手写HashMap加双向链表。链表的节点要同时持有key和value因为删除最久未使用节点时需要通过节点里的key去map里删除对应项只存value的话删不掉map中的key。这个细节是手写LRU最容易踩的坑。我在自己代码里还会加上泛型边界和空值判断保证工程可用而不是只给一个玩具实现。2.3 线程交替打印与数组题并发与边界条件另一类常考手写题是线程交替打印。比如三个线程循环打印ABC本质是考察线程协作和精确唤醒。用ReentrantLock加Condition比较直观每个线程持有一个自己的Condition打印完唤醒下一个线程、等待自己下次被唤醒。ReentrantLock lock new ReentrantLock(); Condition conditionA lock.newCondition(); Condition conditionB lock.newCondition(); Condition conditionC lock.newCondition(); // 线程A打印A - signal B - await A // 线程B打印B - signal C - await B // 线程C打印C - signal A - await C这道题考的不是会不会写多线程而是你在并发场景下有没有“死锁敏感度”。等、唤醒、再等这个循环里控制好state标记和signal的顺序才不会空唤醒或永久等待。我当年在考场写完之后会额外检查一个边界循环次数是10次时最后一个线程执行完不应该再await否则主线程会卡死。所以循环体内的await要放在循环条件判断之后或者用计数控制退出。数组类题相对温和合并两个有序数组、二分查找变种是高频原题。我的策略是先把边界条件写清楚空数组、长度为1、重复元素、目标不在数组中然后在代码里用显式的条件注释标出来。笔试阅卷不一定执行代码面试官主要看你解题思路和防御式编程的意识所以光写一个能跑的主流程不够展示边界处理更拿分。3. 笔试之外面试追问里的Android机制深水区3.1 代码混淆与R8从考点到工程落地笔试的选择题里偶尔会出现“混淆的作用”这种送分题但面试官更常追问你的项目是怎么做混淆的ProGuard和R8有什么区别线上崩了一个混淆后的堆栈怎么还原。这些问题如果在笔试后的技术面答不上来前面的笔试题相当于白做。R8是AGP 3.4之后默认开启的压缩优化器整合了D8和ProGuard的能力在混淆的同时会做代码裁剪、内联、类合并。混淆规则文件里最基础的是keep住入口类-keep public class * extends android.app.Activity -keep class com.jd.example.bean.** { *; } -keepattributes Signature, *Annotation* -keepclassmembers class **.R$* { public static fields; }第一行保证所有Activity类名不被混淆因为AndroidManifest里的类名是字符串引用混淆后找不到会直接崩。第二行通常保留数据模型类因为Gson这类库要反射创建实例字段名混淆后JSON映射就断了。第三行是为了反射和注解框架预留的。混淆后最怕的就是线上堆栈变成a.b.c.a这种字母。AGP在构建时会生成mapping.txt文件线上崩溃日志收集上来之后用SDK里的retrace工具或者proguard-retrace脚本还原。我团队里踩过一次坑是释放包时没有把mapping文件归档到CI产物里线上出的问题花了两个小时才定位。所以现在我对所有release构建都会强制保留mapping文件并备份到对象存储这个习惯比写混淆规则本身更重要。3.2 AMS与Activity启动链路能讲到什么深度决定差距笔试题考Activity启动模式其实是在为面试官留钩子。面试环节十有八九会追问一次startActivity系统到底做了什么。基础答案是调用ActivityManager.getService().startActivity由AMS完成权限校验、进程检查、任务栈操作。如果目标Activity所在进程还没启动AMS会通过Zygote进程fork出新的应用进程然后在新进程里执行ActivityThread.main走到handleLaunchActivity最终触发onCreate。如果面试气氛好我会再补两个细节。一是AMS运行在system_server进程App进程和系统服务的通信走Binder真正执行Activity生命周期的是App进程里的ActivityThread所以Activity的onCreate不是在AMS里执行的。二是从Android 10开始谷歌把Activity栈管理职责从AMS拆分到了ActivityTaskManagerServiceAMS从原来的一把抓变成了更专注进程和组件管理的角色Android 12之后还有更细的划分。这个演进能讲清楚说明你平时关注过系统源码而不只是背八股。2019年的Android面试语境下能讲清楚AMSBinderZygote这三角关系已经能超过大多数候选人。现在再面试的话最好把ATMS和窗口管理WMS的关系也理一遍因为大厂已经普遍在问。3.3 OTA升级与系统版本适配客户端开发眼里的系统级机制京东这类电商App用户设备碎片化严重系统版本从Android 5.0到最新版都有存量所以面试官问到OTA相关问题时重点不在recovery升级流程而在你如何处理版本跨度带来的适配问题。Android 10之后存储权限分区存储强制生效targetSdkVersion升到30以上的App不能再随意访问外部存储公共目录。Android 13的POST_NOTIFICATIONS需要运行时权限Android 14对前台服务类型做了更严格的限制后台启动Activity也受限。这些变化叠加OTA升级后用户手机系统是从旧版直接升上来的App之前申请的权限可能被重置targetSdkVersion可能因为应用商店策略被迁移提高如果没有做好兼容就会出现“系统升级后App闪退”的舆情问题。系统层面还有一个容易被忽略的点Android 10引入了APEX模块格式OTA升级可以只更新系统模块而不刷完整固件。作为App开发者不需要深入到APEX内部但要知道系统组件可以独立升级所以调试某个系统API问题时不能只认定“用户设备系统版本老”可能厂商已经把个别模块热更过了。能意识到这个层级面试官会觉得你有系统全局观。3.4 蓝牙、车载与多端延伸技术视野里的加分项京东笔试不会直接考BLE开发但面试环节偶尔会聊到业务场景。比如京东物流、门店设备里有大量蓝牙打印机、扫码枪车载场景也需要手机和车机互联这些业务都建立在Android系统能力之上。蓝牙开发的核心知识点其实不多扫描、配对、GATT连接、Service与Characteristic读写。但要注意Android 6.0之后定位权限和蓝牙权限是绑定的Android 12之后蓝牙相关权限又从定位权限里拆了出来适配逻辑经常变。车载方面AAOS和Android Auto的生态对普通开发者是陌生领域但掌握CarAppLibrary的基本架构能在面试里展示自己可以承担跨端业务。这些内容不一定是笔试考纲但和技术视野相关。建议备考后期按“原生开发为主系统级与跨端能力为辅”的思路扩展不用强行造轮子知道主流方案和适用场景就够了。4. 工程化工具链Android Studio与构建系统的隐藏考点4.1 AGP版本与Gradle版本必须对应很多人笔试成绩不错却在实习入职第一天被Gradle Sync卡住原因其实很简单AGP和Gradle版本不匹配。2019年前后这个问题已经存在放在今天更是高频翻车点。比如Android Studio Hedgehog 2023.1.1 Patch 2这个版本很多人问它到底支持AGP 8吗。答案是支持的Hedgehog内置了AGP 8.1并且向下兼容到AGP 3.2左右的版本但具体能用的组合还要看官方兼容表。我常用的版本对应关系大致是Android Studio版本可用AGP版本范围Gradle最低版本Hedgehog 2023.1.13.2 ~ 8.28.0Flamingo 2022.3.13.2 ~ 8.18.0Giraffe 2022.3.13.2 ~ 8.18.0Electric Eel 2022.1.13.2 ~ 7.47.5注意这只是一个参考口径每次升级之前还是要查官方文档里的AGP Release Notes因为同一个大版本内的patch也可能调整兼容性。我的习惯是尽量保持AGP为次新版本不要追最新因为新版本的Lint检查和构建规则收紧可能会导致旧项目大量报错团队协作成本很高。4.2 构建失败排查Gradle同步报错的通用套路一个很典型的报错是“Could not load compiled classes for settings file”我当年第一次见到时也懵了。排查这类问题不要急着去改代码先分清楚是构建脚本编译失败、依赖解析失败还是Gradle缓存损坏。就这个报错来说本质是Gradle尝试加载settings.gradle的编译产物失败常见诱因有三个Gradle daemon缓存坏了、settings脚本含有过期配置、JDK版本与Gradle不兼容。标准处理流程是先清理缓存再试在项目根目录执行./gradlew clean删除.gradle目录里对应项目的缓存然后File菜单里Invalidate Caches并重启Android Studio。还不行就检查JAVA_HOME指向的JDK版本AGP 8.0之后要求JDK 17如果还在用JDK 11这类玄学报错会反复出现。再不行就关掉Configuration Cache在gradle.properties里加一句org.gradle.configuration-cachefalse。如果关掉之后正常了说明是缓存脚本和当前项目配置不一致可以手工删掉项目下的.gradle配置缓存目录。这类排查思路比记住单一报错有用得多。面试官如果问“你遇到过最棘手的构建问题是什么”你要答出清晰的排查链路而不是直接报答案这样才能体现工程能力。4.3 构建工具链演进给我们的启示从Eclipse ADT时代到Android Studio Gradle时代再到现在Compose全面普及构建工具链变化非常快。但底层逻辑没变Gradle是构建编排器AGP是Android的编译适配层R8负责压缩混淆aapt2处理资源D8做字节码转换这些工具链的职责边界比较稳定。我推荐把所有构建操作都沉淀成命令行脚本至少在CI上排查问题时用得到。gradlew assembleDebug、gradlew test、gradlew lint都是日常必备命令。如果有一天Android Studio卡死或构建缓存异常终端命令行反而最可靠。同时建议打开构建扫描功能通过Build Scan页面可以直观看到每个Task的耗时定位性能瓶颈比瞎猜高效得多。工具链这一块不是笔试直接考点但它是笔试之后的生存技能。校招生入职第一个月能不能独立拉代码、起环境、跑CI流程基本就看这部分功底。5. 从京东这套题反推Android校招备考的模块化清单5.1 模块一Java基础与并发集合类的底层实现必须要能画出图来。HashMap的put流程要讲到数组、链表、红黑树、扩容因子、树化阈值ArrayList要能说清扩容到1.5倍。并发方面synchronized和volatile的区别、ReentrantLock的实现、AQS的同步队列、ThreadPoolExecutor七个参数逐个过一遍。自测标准能十分钟内默写线程池的execute方法大致执行流程并且能解释核心线程数、队列、最大线程数的关系。5.2 模块二Android四大组件与Handler四大组件各自的启动流程要能串起来。启动模式、任务栈、onNewIntent、进程优先级、IntentFilter匹配规则这些都是选择题的题库来源。Handler部分建议自己写一个Android主线程为什么不会因为Looper死循环而卡死的解释。自测标准能对着手机桌面上的一个App图标讲清楚点击图标到MainActivity可见这一整条链路里AMS、ActivityThread、WindowManager分别做了什么。5.3 模块三View体系与动画View的测量、布局、绘制三步要能说清事件分发要理解Activity、ViewGroup、View三级分发机制和onTouchEvent的返回值影响属性动画和View动画的本质区别补间动画只是改变绘制效果属性动画真正改的是属性值。现在如果准备面试还建议把Compose的基础重组机制看一遍虽然不一定是必考项但已经成为很多团队新项目的默认方案。5.4 模块四性能优化与稳定性从内存泄漏的分类开始静态持有Activity、Handler内存泄漏、匿名内部类持有外部实例、注册没反注册每一种都要能给出代码角度的规避方案。ANR要分清输入事件超时、广播超时、Service超时三大类型并知道用adb shell am dump hang命令抓取ANR trace。自测标准同事塞给你一个卡顿现场你能立刻给出采集日志、子线程堆栈、主线程调用链的三步方案。5.5 模块五网络、存储与安全HTTP与HTTPS的区别HTTPS握手的证书验证流程OkHttp的拦截器链顺序Glide的三级缓存和生命周期绑定这些都是问烂了但依然有效的题目。存储方面要掌握SharedPreferences在高频写入场景下的问题以及DataStore、MMKV等替代方案。安全方面要了解混淆、加壳、签名校验的基本思路至少能说出为什么纯Java层的签名校验可以被绕过应该把校验放到Native层。5.6 怎么把清单落到实处模块清单列出来之后不要排成每天学几个小时的填鸭计划。我自己的做法是每周末抽一套真题掐时间完成选择题部分然后分析错题对应的是清单里哪个模块再集中攻那一块。周中用碎片时间看源码重点看Handler、ActivityManagerService、OkHttp三个框架的源码结构不需要逐行读看调用链和关键类职责即可。这样坚持六到八周基础会比零散刷题扎实很多。写到这里我自己也回想了一下能拿到京东这类公司offer的候选人往往不是刷题最多的而是能把“原理”和“代码”互相翻译的人。笔试题只是入口真正考核的是你被装进项目后能不能把一块复杂功能稳定做出来。如果你正在准备校招建议别把重心全放在背答案上学一道题就动手把Demo写出来把面试官可能追问的下一层问题也准备到。这个习惯会比多刷十套题更值。后面我还会按照同样的复盘思路继续拆其他大厂的Android真题把每年的考点变化和面试风格记录下来希望对你有用。