ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

货拉拉Android笔试题全解析:从Handler机制到性能优化

货拉拉Android笔试题全解析:从Handler机制到性能优化 拿到这套题的时候我正在整理旧电脑里的面试资料翻出来一看2018年的货拉拉Android秋招笔试题卷B。虽然标题带着年份但说句实话把里面90%的考点换成现在的面试题也完全成立——Java基础、Android四大组件、Handler机制、网络协议、性能优化这些年考察的内核基本没变过。而货拉拉这类做货运物流的互联网公司笔试出题还有一个鲜明的行业特征LBS定位、订单状态流转、弱网环境下的大文件传输、IM消息即时性、地图轨迹纠偏。这套逻辑贯穿了它的整套笔试题从选择题的侧重点到编程题的场景都能感受到。这篇文章我就从这套卷子切入把当时的高频考点、答题思路、容易丢分的坑以及笔试完之后面试官会沿着哪些方向追问都整理出来。不管你今年参加秋招还是社招换坑技术基础部分的备考路径基本都是这条路题是旧的但考察点历久弥新。1. 先搞懂货拉拉这类公司的笔试到底在筛选什么1.1 物流货运类App的家底决定了笔试题的考察方向货拉拉的业务核心是什么用户下单、司机接单、货物运输、在线支付、轨迹追踪、消息沟通。这套业务落在Android端技术难点非常集中地图SDK集成与轨迹平滑、订单状态在多个页面间的同步一致性、司机和用户之间的IM聊天、偏远地区弱网环境下的请求超时与断点续传、长时间后台运行的耗电管理。所以你看它的笔试出题不会像纯社交App公司那样偏重复杂的自定义View和动画也不会像大厂基础架构组那样一上来就考插件化、热修复原理。它的考点是基础扎实 业务场景敏感。意思是Java和Android的基础知识必须过硬但光过硬还不够你得能把这些基础往“定位、订单、消息、弱网、性能”这几个方向上去联想。比如一道简答题问“Handler机制的原理”如果你只回答了Handler、Looper、MessageQueue三者关系只能算及格。要拿高分你得主动补一段在主线程里为什么不能用Handler做耗时操作Looper.loop()为什么不会导致主线程卡死子线程里要用Handler该先做什么。这些延伸点在物流App的IM场景和轨迹上报场景里都有直接对应——消息轮询、批量上报、线程切换本质都是Handler机制的业务化。1.2 两年经验的候选人和应届生在同一张卷子上的差距在哪2018年秋招这批岗位有些是校招有些是面向1-3年经验的社招笔试用的可能是同一套题。当时我在圈子里和人复盘过这套卷子发现一个规律应届生和社招候选人的分数差异不在选择题和填空题而在简答题和编程题的表达深度。选择题考的是“知不知道”比如HashMap的时间复杂度、TCP和UDP的区别、四种启动模式的特点。这类题背过八股的人都能拿分区分度有限。真正拉开差距的是简答题里的“结合场景说明”和编程题里的“谈谈你的优化思路”。举一个当时印象很深的例子。卷子里有一道简答题”ListView加载大量图片时为什么会卡顿你怎么优化“应届生常见的答案是用ViewHolder、用Glide加载图片、做内存缓存。这些都对但只答到第一层。有经验的候选人会继续往下说ViewHolder解决的是findViewById的重复调用问题本质是减少View树的遍历开销图片加载的卡顿主因是主线程解码大图Glide做了RGB_565降级、采样率缩放、磁盘缓存但还要注意Glide默认缓存的是转换后的图片原图缓存要单独配更关键的是数据源分页、滑动时暂停加载、停止滚动后再加载以及图片URL的尺寸裁剪参数这些才是物流列表页订单列表、车辆列表真正常用的方案你看同一个问题能不能把原理和业务场景串起来分差就出来了。所以你在准备笔试时别光背结论多问自己一句这个知识点在我投递的业务方向里具体用在哪2. 卷面结构拆解从选择填空到简答编程每部分在考什么2.1 选择题和填空题的高频考点分布这套卷子我对完答案后做了个统计选择题填空题大概占总分的40%考点分布非常典型考察模块具体考点出现频率Java基础HashMap原理、与equals区别、String不可变性、强软弱虚引用、异常体系高并发编程synchronized与volatile、线程池参数、ThreadLocal、死锁条件中高Android基础Activity启动模式、Service两种启动方式、Handler机制、BroadcastReceiver注册方式高内存相关内存泄漏常见场景、OOM触发条件、垃圾回收机制中网络协议HTTP状态码、TCP三次握手、HTTP与HTTPS区别中数据结构链表反转、二叉树遍历、栈和队列应用中有一道填空题让我印象很深”在Java中HashMap的默认初始容量是____默认加载因子是____。“这道题本身不难答案是16和0.75但它的坑在于后续——很多人在简历里写”熟悉Java集合类“却在答案里把二者记混。更深的关联是加载因子0.75是时间和空间复杂度的权衡如果设成1空间利用率高但冲突概率变大设成0.5冲突少但浪费空间。这个权衡逻辑比你记住0.75本身更有笔试价值因为面试官会在你笔试之后继续追问这句。另一个高频点是Activity生命周期。填空题很喜欢考”当Activity A启动Activity B时二者的生命周期回调顺序是____。“标准答案是A.onPause - B.onCreate - B.onStart - B.onResume - A.onStop。但如果你只写这一层遇到业务场景题就会吃亏。物流App里常见的情况是从订单详情页跳转到地图选点页如果选点页是半透明主题的ActivityA的onStop根本不会调用只会停在onPause。很多消息推送、定位启停的逻辑就要放在onStart/onStop而不是onResume/onPause里做。这个细节笔试不会直接考但会在下一轮面试里等着你。2.2 简答题不是背八股是看你能把原理讲多透简答题大概占30%-35%每道题5-10分不等是最能看出”工程直觉“的部分。这类题有几个高分特征第一有结构。先给结论再分点展开最后补充边界条件。比如问“怎么避免内存泄漏”你按这个顺序答先说明什么是内存泄漏长生命周期对象持有短生命周期对象引用导致GC无法回收再列常见场景静态变量持有Context、Handler持有Activity、内部类持有外部类、资源未关闭每类给出解决方案最后补一句“如何排查——用LeakCanary或Android Profiler的Memory Profiler”。结构清晰踩分点全覆盖。第二有场景。简答题里如果出现“结合你熟悉的业务场景”千万别空着。哪怕你对物流行业不熟也可以拿一个自己做过的项目来举例。比如问“你如何做列表页的流畅度优化”你不能只答RecyclerView的复用机制还要说明你具体在哪个页面做了哪些优化措施达成了什么效果——订单列表一屏滑动掉帧率从多少降到多少、加载更多时分页大小设了多少、每秒滑动多少像素时开始暂停图片加载。这些数字就是经验值比背诵十条优化原则有效得多。第三有反思。有些简答题最后会问“你有什么方法验证你的优化效果”这其实在考察你有没有闭环思维。回答里带上“用Systrace抓卡顿、用TraceView看方法耗时、用BlockCanary监控主线程卡顿、对比优化前后帧率曲线”这类话会让阅卷人觉得你是真的搭过性能优化这个体系而不是背了几条优化口诀。2.3 编程题从卷B的实际风格看命题思路编程题一般占25%-30%可能是纯算法题也可能是Android工程题。2018年的卷B里编程题的风格不是LeetCode那种纯靠算法竞赛刷出来的类型而是偏“工程可用”和“边界处理”的手写代码题。我当时整理了这类题目的四种常见形态这些年刷题也一直在验证这个分类数据结构基本功型链表反转、合并两个有序链表、二叉树层序遍历、判断括号是否合法。这一类考的是你有没有扎实写过基础代码。Java/Android机制模拟型用Handler实现倒计时、手写生产者消费者模型、自实现一个简单的图片内存缓存LRU。这一类考的是你能不能把原理变成代码。业务场景轻算法型比如“司机和货物匹配的简单贪心方案”“订单列表去重与排序”“轨迹点的抽稀”。这一类考的是你有没有把业务问题抽象成代码的能力。工程细节型给定一组订单数据让你用代码实现分页加载的状态管理包括加载中、加载成功、加载失败、没有更多数据四种状态。这一类考的是你的工程化思维和代码健壮性。不管哪种类型阅卷人看的不只是你的答案对不对更重要的是代码规范、边界考虑、变量命名和注释习惯。我见过很多候选人算法思路完全正确但代码没有判空、方法的英文命名语法错误、大段逻辑挤在一个for循环里备注都不写这种即使跑通用例分也不高。3. 容易被扣分的软肋题网络、并发与性能优化3.1 网络协议题别只答概念要把场景带进去物流App的网络环境比一般App更恶劣。司机在县城、高速、地下车库送货网络经常断断续续所以笔试题对网络层的考察不会停留在“什么是HTTP”这个层面而是会深入到弱网处理方案上。我当时整理网络类高频题的时候发现几条必背的硬知识点HTTP的请求方法、状态码含义2xx、3xx、4xx、5xx至少要能说出302、304、400、401、403、404、500、502、503的含义TCP三次握手和四次挥手的过程为什么要三次而不是两次HTTPS的握手过程非对称加密和对称加密分别用在哪个阶段数字证书的作用但只背这些不够。货拉拉的卷子很可能在简答题或选择题里植入场景“司机在弱网环境下上传图片失败你会怎么设计重试机制”这种题的答题套路是基础重试失败后指数退避比如1秒、2秒、4秒、8秒防止服务端被打爆分片上传大文件切成小块记录每块的上传状态断点续传队列管理把上传任务放到线程池管理区分前台任务和后台任务状态上报用WorkManager或Service保证上传任务在App进程被杀死后能恢复你把这些分层列出来阅卷人一看就知道你对弱网处理不是只背过概念而是真的在系统层面思考过这个问题。3.2 并发编程从语法背到执行逻辑并发这块选择题爱考synchronized和volatile的区别、线程池的几种拒绝策略、死锁的必要条件。简答题爱考“让你设计一个从网络加载图片的框架你会如何考虑并发”。后者是典型的“把考点藏在工程里”单独背并发八股的人会懵。我当时建议候选人准备时把并发知识点都往“线程池”这个载体上靠因为线程池几乎是所有并发问题的交集核心线程数、最大线程数、队列容量怎么设置拒绝策略AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy分别在什么时候用图片加载框架里为什么加载任务要划分优先级屏幕中间的优先、边缘的延迟加载为什么用ArrayBlockingQueue而不是无界LinkedBlockingQueue因为无界队列可能导致内存撑爆图片加载场景必须有限流。再加一个synchronized和volatile的经典追问volatile能保证可见性和有序性但不能保证原子性synchronized三者都能保证。再往下锁的升级过程无锁-偏向锁-轻量级锁-重量级锁虽然笔试很少考到但面试一定会追到我建议你趁早一起准备了。3.3 性能优化答出“排查链路”而不是“知识点列表”性能优化一直是货拉拉笔试里比较重的出题方向因为它的业务实在是太吃性能了地图拖动、列表滚动、轨迹绘制、后台定位每一个都在挑战系统资源。我在刷这套卷的时候凡涉及到性能优化的大题几乎都遵循同一条答题链路第一步量化问题。别说“App有点卡”要说“RecyclerView在快速滑动时掉帧率约15%”。要说出你用Systrace或Profile GPU Rendering看到的卡顿集中在哪个阶段。第二步定位瓶颈。卡顿无非四种来源主线程做了耗时操作、布局层级过深导致Measure/Layout过慢、View过度绘制、内存抖动导致GC频繁。你要根据工具定位是哪种。第三步针对性优化。布局优化用ConstraintLayout、避免嵌套LinearLayout、include和merge标签绘制优化用setWillNotDraw、清理不必要的背景内存优化用对象池、复用Bitmap、警惕Handler泄漏异步化用WorkManager、线程池、协程。第四步结果验证。优化不是做完就完了要有数据支撑掉帧率降了、启动时间从多少降到多少、内存占用峰值降了多少。我见过很多候选人答题时只写到第三步把优化手段列得飞起就是不量化。这其实是所有性能优化题的通用得分点你写一个“优化后掉帧率从15%降到3%”比写十条优化原则都管用因为它在告诉阅卷人我真的做过这件事我清楚我的改动带来的收益。4. 编程题实战演示从读题到自测的完整答题流程4.1 题目合并两个有序链表这种题在LeetCode上是简单难度但笔试时失分点比想象中多得多。当时有个很有意思的现象思路大家都会但代码交上来的质量参差不齐。问题最多的不是逻辑写错而是没考虑空链表、循环里对空指针的保护不够、代码风格散漫。我以这道题为例演示一下一个“能拿高分”的答题过程。第一步先写思路。我会在答题区先写两行注释// 递归每次比较两个链表头结点较小的作为当前节点next指向剩余链表合并结果 // 时间复杂度 O(mn)空间复杂度 O(mn)递归栈然后写递归版本class ListNode { int val; ListNode next; ListNode(int x) { this.val x; } } public ListNode mergeTwoLists(ListNode l1, ListNode l2) { if (l1 null) return l2; if (l2 null) return l1; if (l1.val l2.val) { l1.next mergeTwoLists(l1.next, l2); return l1; } else { l2.next mergeTwoLists(l1, l2.next); return l2; } }这版代码能拿到基础分但它不是最优解。笔试时间有余力的情况下我会再补一个迭代版本因为递归版本的缺点在链表很长时有栈溢出风险而迭代版本空间复杂度是O(1)public ListNode mergeTwoLists(ListNode l1, ListNode l2) { ListNode dummy new ListNode(-1); ListNode cur dummy; while (l1 ! null l2 ! null) { if (l1.val l2.val) { cur.next l1; l1 l1.next; } else { cur.next l2; l2 l2.next; } cur cur.next; } cur.next (l1 ! null) ? l1 : l2; return dummy.next; }两个版本都写出来备注里加一句“递归版代码简洁但空间复杂度为O(n)迭代版使用哨兵节点避免空指针判断实际工程中更推荐”。这不只是炫技它告诉阅卷人你有工程权衡的思维——笔试里最不值钱的答案是只有一个“能跑的答案”。4.2 题目手写一个LRU缓存这是2018年秋季Android岗笔试里出现概率极高的题货拉拉的卷子也没放过它。因为图片内存缓存、二维码缓存、路由表缓存每一个都能用到LRU。答题时我建议直接上LinkedHashMap的LinkedHashMap构造方法配合accessOrder开启访问顺序。但你要能讲清楚为什么LinkedHashMap适合做LRU——它内部维护了一个双向链表accessOrder为true时每次get或put都会把节点移动到链表尾部LRU的“最近最少使用”正好对应“链表头部就是最久未访问的节点”。一个能拿到高分的实现import java.util.LinkedHashMap; import java.util.Map; public class LRUCacheK, V extends LinkedHashMapK, V { private final int maxCapacity; public LRUCache(int maxCapacity) { super(maxCapacity, 0.75f, true); this.maxCapacity maxCapacity; } Override protected boolean removeEldestEntry(Map.EntryK, V eldest) { return size() maxCapacity; } }这版代码很短但踩分点很密集构造参数的三个值分别是什么含义初始容量、加载因子、accessOrder为什么复写removeEldestEntry每次插入后判断是否超过容量超过就淘汰头部节点。如果笔试题目里明确要求不能用LinkedHashMap那就手动实现一个HashMap双向链表链表节点保存key和valueHashMap存key到节点的映射get时把节点移到尾部put时如果超过容量就删除头部节点。我当时建议候选人把两道都写一遍因为面试追问大概率是“你说你熟悉LRU那如果让你用HashMap双向链表实现你怎么保证时间复杂度是O(1)”你没亲手写过这题一定会卡壳。4.3 编程题答题的通用检查清单做完编程题后别急着交卷花两分钟做一遍自查这些都是阅卷人会看的隐藏项有没有判空链表、数组、字符串参数都可能传null你的代码第一行有没有保护有没有处理边界比如数组长度为0、链表只有一个节点、数字溢出。变量命名有没有语义a、b、c这种命名是自杀式行为如果阅卷人看不懂你在写什么你逻辑再对也是无效分。有没有写时间复杂度和空间复杂度这两行注释在笔试中是最便宜的送分项但很多人忽略。代码缩进和括号风格是否统一一个缩进乱七八糟的代码块即使跑通了用例阅卷人也会认为你的工程习惯不好。5. 这套卷子的答题节奏与卷面策略5.1 拿到卷子先花两三分钟做战略分布笔试题量一般不小选择和填空可能就有20-30道外加5-6道简答2-3道编程。如果一上来就从第一道选择题开始硬啃很容易在某个卡壳的题目上耗时过久最后编程题只剩十分钟草草收尾。这是笔试最大的失分原因。我的习惯是拿到卷子先快速通读一遍所有题在卷面刻个“轻重缓急”的标记。第一优先级是你会做的、拿分稳的选择题和填空题快速扫一遍就填第二优先级是简答题里你能答出结构和场景的第三优先级是编程题尤其是那种思路明确但代码量大的要留出整块的连续时间。对于完全不会的题别空着。选择题蒙一个也要填满填空题能写半个公式也好简答题哪怕只写出相关知识点也比你交白卷强。笔试阅卷是踩点给分你写出的每一个有效信息都有概率成为得分点。5.2 简答题写多少字合适很多候选人纠结简答题篇幅。写太少怕踩分点不够写太多怕时间不够还容易暴露逻辑漏洞。我的经验是按分值定篇幅。5分的题写3-5个要点每点两三句话加一个举例就够了10分的题建议分两层先是结论性的骨架再是每个骨架点下的展开和场景化描述。举个例子题目“谈谈你对Android消息机制的理解以及Handler在子线程中的使用注意事项。”低分回答“Handler用于线程间通信子线程可以通过Handler通知主线程更新UI。”高分回答结构Handler机制的本质线程间通信一套由Looper、MessageQueue、Handler三个角色组成的消息循环系统主线程默认有LooperActivityThread.main中调用Looper.loop()子线程需要手动调用Looper.prepare()和Looper.loop()子线程使用Handler的两种方式一是新建Thread并prepares个Looper二是用HandlerThread它内部封装好了Looper循环注意事项消息队列中的消息会持有Handler引用如果Handler持有Activity容易内存泄漏所以要用静态内部类WeakReference补充Message.obtain()复用消息对象避免每次new Message这个结构既完整又有深度比一上来就掉进细节里强很多。阅卷人打分时也是按这种层次感来的。5.3 卷面排版字迹工整就是加分项线上笔试还好打字排版是系统默认的但如果是线下笔试手写的卷面整洁度真的很影响阅卷体验。代码题的缩进、注释位置、箭头指向甚至画图辅助说明都能让阅卷人觉得这个人思路清晰。我见过一个候选人画了一张Handler机制的流程图放在简答题旁边这个附加说明直接让他在所有同分卷子里脱颖而出——阅卷人也是人你让他看得舒服他就更愿意给你找分。6. 从笔试答案延伸出的面试追问清单6.1 笔试只是前菜面试官会沿着你写的答案往下挖这是最容易被忽视的一条经验。很多人交完笔试卷子就觉得完事了但事实上面试官手里大概率有你的答卷他们会在面试时挑你写过的答案追问。尤其是你答得模棱两可的知识点比如选择题里你蒙对的题、简答题里你刚好一句话带过的难点这些都可能是面试环节的爆点。所以备考笔试的时候最聪明的策略是把你写在卷子上的每个关键词都当成一个面试题去准备。我给你列几条常见的“笔试-面试”联动追问线笔试写了内存泄漏的场景静态变量持有Context面试追问静态变量为什么能导致GC无法回收Activity被销毁后为什么Context还在你平时用什么工具检测内存泄漏笔试写了Handler机制面试追问Handler的同步屏障是什么IdleHandler什么时候执行一个消息入队后到分发执行之间的完整流程是怎样的笔试写了HTTPS面试追问说下证书验证的完整流程中间人攻击怎么防御客户端如何做SSL Pinning笔试写了图片加载优化面试追问Glide的缓存分几层内存缓存用的是什么数据结构磁盘缓存的默认大小是多少6.2 写在简历上的每一项都得能扛住三轮追问这套卷子我复盘完最大的一个感触是笔试考的是基础但基础背后是体系。很多候选人基础不差但知识是碎片化的、点状的。笔试选择题能考个七八十分一到面试问“为什么”“怎么做”“还有没有更好的方案”就塌了。我建议准备阶段用“三层追问法”过每一个知识点第一层是是什么第二层是为什么第三层是在什么业务场景下怎么用如果场景变化了怎么取舍。把每个考点都过到第三层笔试题再变形你也不怕。拿“Handler”举例是什么Handler是Android消息机制的核心类通过Looper、MessageQueue完成跨线程通信为什么主线程不能执行耗时操作子线程不能直接更新UI所以需要一套线程间消息传递机制怎么用子线程通过handler.sendMessage发消息消息进入MessageQueueLooper不断取出并在目标线程分发handleMessage中执行更新场景变化如果消息量很大、频率很高比如IM聊天页面你怎么优化答案是消息复用、合并批量处理、避免频繁跨线程传递大对象取舍Handler和协程、RxJava的线程切换有什么本质区别每个方案各自的适用场景是什么当你把每个考点都准备到这个深度这套卷子对你来说就不再是考试而是你整个知识体系的体检报告。7. 2025年回看这套题变化与不变7.1 Kotlin、协程、MVVM这些新东西2018年没考但现在是必考2018年秋招时Kotlin在Android官方虽然有支持但绝大多数公司的生产代码还是Java货拉拉的卷子自然也以Java为主。但放到现在Kotlin已经是绝对主流协程替代了大部分回调地狱Jetpack Compose也起势了。如果你拿这套老题当唯一复习资料一定要额外补上这几块Kotlin协程的线程切换原理和Handler、线程池的对比ViewModel的onCleared、Lifecycle组件如何避免内存泄漏Compose的重组机制和传统View刷新的区别Paging3分页库的状态管理Loading、Error、NotLoadingDataStore替换SharedPreferences后的线程安全问题这些虽然不在2018年的卷面上但是同一个考察逻辑的当下版本——用你能拿得出手的现代工具去解决同样的问题只是答案换了写法。7.2 基础这件事永远没有过期时间十几年Android开发下来我依然认为框架每年都在变但Java内存模型、数据结构、网络协议、四大组件的生命周期、消息机制这些底层知识一点没变。面试官永远都在拿这些底层知识试探你“是不是只会调API”。所以这套2018年的卷子在今天依然有很高的参考价值。它不是用来背答案的而是用来校准自己的知识体系有没有漏洞。我建议你拿到卷子后先自己做一遍然后别急着对答案试着给每道题写一句“这道题在考我什么底层能力”。比如考HashMap是在考你对数据结构和哈希冲突的理解考Handler是在考你对线程模型的理解考网络协议是在考你对全链路数据流转的理解。这样做完一遍你会发现自己真正薄弱的不是某个知识点而是某一类底层能力。我当年把这些考点串成了一张知识图谱按“语言基础-并发-Android机制-网络-性能优化-工程化”六个维度归类每个维度下挂上对应的笔试题和面试追问。备考的时候不看零散题目只看这张图谱效率比盲目刷题高得多。这套方法我也一直在用后来不管是自己面试还是帮别人做模拟面试都靠这张图谱兜底。
返回列表