尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

小鹏Android秋招笔试复盘:从Handler到Binder的核心考点解析

小鹏Android秋招笔试复盘:从Handler到Binder的核心考点解析 2020年秋招那会儿我投了好几家造车新势力小鹏算是其中行动比较快的一家。简历刚投出去没两天就收到了笔试邀请链接。说实话手机App端和车机Android系统的开发岗位在那年突然吃香起来很多做客户端的人都在观望这个赛道。我也一样既想看看智能汽车相关的技术栈到底考什么也想借笔试检验一下自己的Android基础到底扎不扎实。这份笔试整体做下来最大的感受就是它不像有些大厂那样专考偏题怪题而是把Android开发里最容易忽视、但又最见功底的知识点全翻出来过了一遍。技术栈集中在Java基础、Android四大组件、Handler消息机制、View体系、性能优化这些核心模块再配一道或两道算法编程题难度中等偏上区分度很高。如果你正准备Android秋招或者打算跳槽到车载/智能座舱方向这篇文章值得认真看完我会把每一个考点的答题思路、底层原理和常见失分点全部拆开讲清楚。1. 笔试全貌90分钟、四类题型、考的不只是写代码1.1 笔试基本信息与流程还原小鹏2020届秋招的Android岗笔试我记忆中是在牛客网的在线笔试系统上完成的整体时长90分钟题量不算特别大但覆盖面广想全做完还得合理分配时间。整个流程是先收到邮件或短信通知点击链接进入笔试房间环境是浏览器在线代码编辑器不支持本地IDE所以平时如果依赖IDE的自动补全考场上的手感会有一定影响。进入系统后有简单的摄像头监考和屏幕录制提示然后就是倒计时做题。整个过程不允许切屏切屏次数多了会被警告甚至强制交卷。这个细节建议提前了解因为很多人习惯边查资料边做题但在这种监考机制下你只能依赖自己的知识储备。我当时就是把所有精力集中在题目本身手机放在一边完全模拟真实考试状态。岗位方向我记得是Android应用开发不是车机系统层或底层BSP开发所以笔试内容偏上层应用Java和Android框架知识占比很高C/C、Linux内核这些基本没怎么涉及。如果投的是座舱系统开发或驱动相关岗位考察内容会完全不同这个后面再说。1.2 整卷结构与分值分布整张卷子大致分为四个板块单选题、多选题、简答题、编程题。单选和多选混在一起大概有20道左右每题2到3分覆盖Java基础、数据结构、Android基础。简答题一般两道每道10分左右需要手写分析过程或文字阐述。编程题一到两道分值20到30分通常一道easy到medium难度的算法题偶尔会结合Android场景比如设计LRU缓存这类计算器/缓存管理问题。从分值分布就能看出基础题占了将近一半编程题再拔高一部分简答和论述题决定你能不能在同等基础的人里脱颖而出。这也是很多人的误区以为笔试就是刷算法实际上选择题和简答题如果错误率高编程题做得再好也很难进面。我的策略是先快速做完选择简答题写要点最后留充足时间写算法题这个顺序帮我稳住了整体节奏。下面我把每个板块的考点和答题思路逐一拆开结合我在实际笔试题里遇到的知识点做一个可复用的复盘。2. Java基础选择题高频考点与易错点全梳理2.1 集合框架ArrayList、LinkedList、HashMap的底层差异这类题目几乎每次笔试必考小鹏也不例外。我当时遇到的选择题里考察点集中在ArrayList扩容机制、HashMap在JDK 1.8下的数据结构变化、以及LinkedList适合什么场景。如果只是背结论很容易在变形题上翻车。先说说ArrayList的扩容。它的默认容量是10当元素数量超过当前容量时会通过grow()方法扩容为新容量的1.5倍也就是oldCapacity (oldCapacity 1)。这里有个细节扩容后会调用Arrays.copyOf把原数组元素拷贝到新数组这个拷贝过程在有大量元素时是有性能开销的。所以如果你能预估数据量直接指定初始容量可以避免反复扩容。HashMap的考点更细。JDK 1.8之后HashMap底层是数组链表红黑树当链表长度超过8且数组容量大于等于64时链表转化为红黑树目的是把最坏情况下的查找时间复杂度从O(n)降到O(log n)。加载因子默认是0.75数组容量始终是2的幂次这背后涉及哈希散列和取模运算的优化很多人只记住了结论没有理解为什么但笔试中遇到“为什么容量必须是2的幂次”这种问法时就得从位运算角度解释。我当时做这组题目时的经验是不要死记硬背各种默认值要理解设计动机。比如加载因子为什么是0.75因为太高会导致冲突加剧太低会浪费空间0.75是空间和时间的一个折中。如果笔试考到类似的变形题你把这个逻辑讲清楚分数基本就稳了。2.2 抽象类、接口与多态别再背定义要会分析场景Java面向对象这块笔试选择题很喜欢出一种题给一段代码问你输出结果是什么。看似简单其实涉及到继承、多态、静态绑定和动态绑定。比如有一个父类和一个子类子类重写了父类的非静态方法再通过父类引用调用这个方法实际调用的是子类的实现。这就是动态绑定。抽象类和接口的选择题在小鹏笔试里出现过类似“以下哪个类可以定义构造方法”这种细节题。要明确回答这个问题需要掌握几条核心规则抽象类可以有构造方法接口在JDK 1.8之前不能有构造方法。抽象类可以定义普通成员变量接口中的变量默认是public static final。一个类只能继承一个抽象类但可以实现多个接口。JDK 1.8之后接口可以有default方法和static方法JDK 1.9之后还可以定义private方法。这种题目考的是你对语言特性的熟悉程度。很多人在开发中只写实现类很少主动设计接口和抽象类所以对两者的区别停留在概念层面。建议临近笔试前把语法层面的边界点看一遍尤其是接口默认方法的多继承冲突规则。还有一个高频点就是String、StringBuilder、StringBuffer的对比。String是不可变的每次拼接都会创建新对象StringBuilder是可变的单线程下效率最高StringBuffer加了同步锁线程安全但性能稍差。笔试中常考String的拼接在循环里会产生大量中间对象这个点以及和equals比较结果的差异。这类题只要把常量池和堆内存的概念弄明白基本能拿下。2.3 线程与线程池核心参数为什么这样设计Android开发中线程无处不在所以笔试里线程相关题目出现频率也很高。小鹏这轮笔试有一道多选题考察的是ThreadPoolExecutor的核心参数。题目大意是以下关于线程池参数的说法哪些正确涉及核心线程数、最大线程数、任务队列的关系以及拒绝策略的执行时机。这道题关键是理解线程池的工作流程当提交一个任务时如果当前线程数小于核心线程数创建核心线程执行如果大于等于核心线程数任务进入阻塞队列等待如果队列也满了且线程数小于最大线程数创建非核心线程执行如果线程数已经达到最大线程数则触发拒绝策略。很多人会混淆一个点核心线程数到底是不是会被回收。默认情况下核心线程不会因为空闲被回收但如果设置了allowCoreThreadTimeOut(true)核心线程在空闲超过keepAliveTime后也会被回收。这个细节在选择题里经常作为一个干扰项出现。四种拒绝策略也需要掌握AbortPolicy直接抛出异常、CallerRunsPolicy由调用线程执行任务、DiscardPolicy静默丢弃、DiscardOldestPolicy丢弃队列中最老的任务。开发中我通常用CallerRunsPolicy因为能在一定程度上降低任务生产速度避免请求直接丢失。把过程和参数对应起来理解笔试里类似的逻辑题就迎刃而解了。3. Android核心机制简答题面试官真正想看到的是什么3.1 Handler消息机制从源码角度回答的注意点Handler可以说是Android笔试必考小鹏这道简答题问的是Handler机制的工作原理以及在子线程中创建Handler需要注意什么。这道题表面上是送分题但答得深入与否区分度极高。我的作答思路是分四层Looper、MessageQueue、Handler、Message。Looper负责循环从MessageQueue中取消息MessageQueue内部是一个链表结构的消息队列通过next()方法阻塞获取消息Handler负责发送消息和处理消息Message是消息的载体。整个机制的核心是一个线程对应一个Looper一个Looper对应一个MessageQueue但可以对应多个Handler。在子线程中创建Handler必须先调用Looper.prepare()初始化当前线程的Looper再创建Handler最后调用Looper.loop()启动消息循环。因为在Android中只有主线程在系统启动时已经自动初始化了Looper子线程默认没有。如果不调用prepare()直接new Handler()会抛RuntimeException: Cant create handler inside thread that has not called Looper.prepare()。我在回答时还加了一个细节点从MessageQueue中取消息时如果没有消息Looper会通过epoll机制让线程进入阻塞状态而不是自旋空转这样既节省CPU又能在有消息时被及时唤醒。这个底层原理值得写进去因为它体现了你对消息机制的理解不止于API使用层面。3.2 Binder机制与IPC选型AIDL的使用场景Android进程间通信IPC是笔试中的常客小鹏这道题给了特定场景如何在两个进程之间传递复杂对象。最佳方案就是使用AIDL但很多人在回答时忽略了关键步骤和边界条件。AIDLAndroid Interface Definition Language是一种接口定义语言用于跨进程通信时自动生成Binder接口的代码。使用时需要经历几个步骤定义.aidl文件、在Service中实现生成的接口、客户端通过bindService绑定服务并获取代理对象。有一点笔试中经常考AIDL支持的数据类型有哪些。包括Java基本类型、String、CharSequence、List、Map、Parcelable对象等。其中自定义的Parcelable对象必须显式import即便是同包下的类也要写import否则编译会报错。这个细节就是个经典埋点。另一个常考点是Binder相比其他IPC方式的优势。它的核心优势是一次拷贝发起方将数据写入内核空间通过mmap映射接收方可以直接读取不需要两次拷贝。这也是Android选择Binder作为核心IPC机制的原因之一。对于车载多屏交互、系统服务调用这类高频通信场景Binder的效率和安全性是很有优势的。我在简答题中还补充了一句话使用AIDL处理并发请求时需要注意oneway关键字的使用。oneway表示异步调用客户端调用后立即返回适合不关心结果的场景而默认的同步调用在跨进程耗时操作时可能导致客户端线程阻塞需要根据业务场景合理选择。3.3 四大组件启动流程与进程生命周期Android四大组件是基础中的基础但笔试里很少直接问“四大组件是什么”而是会给一个具体场景让你判断组件如何交互。小鹏有一道类似的题从点击通知栏到启动一个Activity系统经历了哪些流程以及涉及哪些进程。正确思路是从PendingIntent开始说。通知栏点击后通过PendingIntent发送一个Intent到系统ActivityManagerServiceAMS收到请求后检查目标Activity所在进程是否存在。如果不存在会先通过Zygote进程fork一个新进程然后在主线程中ActivityThread的handleLaunchActivity方法里完成Activity的创建和生命周期回调。这里要重点掌握的是onCreate、onStart、onResume都是在主线程执行的而AMS与App进程之间的通信过程是跨进程的通过Binder完成。很多时候面试官会延伸问一种情况App被杀后Activity重新创建时onSaveInstanceState和onRestoreInstanceState的调用顺序以及ViewModel为什么能在配置变更后存活这两者都是生命周期相关的高频考点。四大组件里的Service和BroadcastReceiver也容易出选择题。比如广播分为普通广播和有序广播有序广播可以被拦截LocalBroadcastManager只能在应用内使用。从Android 8.0开始隐式广播大多数场景下不再允许在Manifest中注册接收只能在代码中动态注册。这些限制是笔试常考的新特性变化建议系统性记一遍。4. UI与性能优化笔试里的高区分度部分4.1 View的绘制流程与事件分发UI相关题目在Android笔试里的位置很特别它不像Java基础那样可以靠背题蒙对而是需要真正理解整个体系。小鹏卷子里有一道选择题问的是requestLayout和invalidate的区别我把这两个概念的边界理清了这对实际开发也很有帮助。requestLayout会触发measure和layout流程但不一定会触发draw因为测量和布局完成后如果视图尺寸和位置没有变化就不会重绘。invalidate只会触发draw流程不会重新测量布局。所以当你只改变view的内容时调用invalidate就够了当你改变view的大小或位置时才需要requestLayout。View事件分发机制也是必考重点核心是三个方法dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent。事件传递顺序是Activity - ViewGroup - View。如果子View的onTouchEvent返回false事件会回溯到父ViewGroup的onTouchEvent再回溯到Activity。如果想拦截子View的事件通过重写onInterceptTouchEvent返回true。当时有一道题考察的是OnTouchListener和onTouchEvent的执行顺序。答案是如果给View设置了OnTouchListener并且它的onTouch方法返回true那么onTouchEvent就不会被调用。这个点很多人在开发中没注意因为很少在同一个View上同时使用两种方式处理触摸事件但笔试就是喜欢考这种边界。4.2 RecyclerView复用机制与缓存层级如果笔试中出现“ListVew和RecyclerView的区别”基本上就是送分题但如果你想拿高分光说“RecyclerView更高效、支持更多布局”是不够的。小鹏这道题的角度是RecyclerView的缓存机制为什么比ListView高效。RecyclerView的缓存分为四级mAttachedScrap、mCachedViews、mViewCacheExtension、mRecycledViewPool。第一级缓存当前屏幕内还在显示、但需要重新绑定的ViewHolder第二级缓存离开屏幕但仍在缓存范围内的ViewHolder默认容量为2第三级是开发者自定义缓存第四级是共享的ViewHolder池可以跨RecyclerView实例共用。这里最容易考到的细节是为什么mCachedViews里的ViewHolder不需要重新执行onBindViewHolder而mRecycledViewPool里的需要。因为mCachedViews中的ViewHolder还保留了之前的视图数据可以直接复用而RecycledViewPool为了节省内存会清除ViewHolder的引用所以复用时必须重新绑定数据。这是RecyclerView设计上的关键权衡也是区分缓存层级的核心逻辑。当时我还在简答题里加了一个优化思路如果一个页面里有多个列表可以通过setRecycledViewPool共享一个pool减少ViewHolder的创建成本。这种方式适合页面结构相似、item类型一致的场景比如外卖App里的多个并列Tab页实测可以在滑动时明显减少卡顿。4.3 内存泄漏排查思路性能优化模块里内存泄漏是笔试和面试的高频话题。但单纯背“内存泄漏就是对象不能被GC回收”这种定义没有意义笔试更希望看到你如何定位和分析问题。常见的内存泄漏场景有静态变量持有Activity引用、Handler里的Message延迟发送、匿名内部类持有外部类引用、资源未关闭BroadcastReceiver未注销、Cursor未close、Animation无限循环等。让我印象深刻的是Handler导致泄漏的那道题非静态内部类Handler默认持有外部Activity的引用如果MessageQueue中有延迟消息未处理完Activity销毁后因被Handler持有无法回收。正确的解决方案有两种一是把Handler定义成静态内部类使用弱引用WeakReference持有Activity二是在onDestroy中调用removeCallbacksAndMessages(null)移除所有消息和回调。我建议两种都做双保险。笔试中如果让你写解决方案最好把两种方案都写出来并说明各自的作用。内存泄漏排查工具也要能说出来Android Studio自带的Memory Profiler可以实时查看堆内存变化LeakCanary是一个自动检测内存泄漏的第三方库适合在debug阶段集成。排查思路是先复现问题再抓取内存快照HPROF文件分析引用链找到不应该持有目标的GC Root最后验证修复效果。这个流程在笔试简答题里非常有价值因为它的每一步都是可操作的。5. 算法题直接上代码的环节5.1 字符串与数组类经典题的思路小鹏的编程题没有太偏门我当时遇到的是一道与数组相关的题目。虽然具体题目内容可能每年变化但这类题型的解题思路是通用的值得系统梳理。数组类常见题型包括两数之和哈希表优化、三数之和排序双指针、最长无重复字符子串滑动窗口、合并两个有序数组双指针从后往前等。如果笔试时间紧张优先把这类题练熟。以两数之和为例暴力解法是O(n^2)但用哈希表可以把时间复杂度降到O(n)。核心思路是遍历数组时把target - nums[i]作为键存入哈希表值存索引后续元素如果在哈希表中命中直接返回两个下标。这个小技巧在各种数组求和类题目里都能复用。滑动窗口类题目也很高频比如求最长不含重复字符的子串长度。核心是维护一个窗口用哈希集合记录窗口内的字符右侧指针不断右移遇到重复字符时就移动左指针缩小窗口。这个算法的关键在于窗口收缩的时机很容易在边界条件上出错笔试要格外小心。5.2 链表类题的边界处理链表相关的题目几乎每年笔试都会出现因为它的考点是明确的指针操作和边界处理。常见的有反转链表、找链表中间节点、删除倒数第N个节点、判断链表是否有环。反转链表是最基础但也最容易出错的题。迭代法的核心是三个指针prev、curr、nextTemp每次循环先保存下一个节点再让当前节点指向前一个节点然后整体后移。很多人写递归版本时容易在基准条件上出错建议优先掌握迭代版本。找链表的倒数第K个节点最优解法是快慢双指针快指针先走K步然后快慢指针同时移动快指针到达尾部时慢指针就是倒数第K个节点。这道题的边界条件是K大于链表长度的情况笔试里通常会考你如何处理看到题目时要主动问面试官或写防护逻辑体现你的工程意识。在造车场景里链表类算法的实际映射是消息队列、事件流处理这类有序数据的增删操作。虽然写业务代码时不一定直接手写链表但理解指针操作对排查数据结构和并发问题非常有用。5.3 动态规划与贪心的判断小鹏笔试的算法题难度整体控制在一道easy一道medium动态规划通常不会出太难的状态转移方程但你要具备识别DP问题的能力。常见特征是求最值、方案数、最长/最短路径并且当前状态依赖前面的某个状态。比如爬楼梯问题dp[i] dp[i-1] dp[i-2]这类题目只要找到递推关系就能解。进阶一点的股票买卖问题核心是每次都在历史最低点买入、当前价格卖出用一个变量记录历史最低买入价其实就是贪心的思想。这类题在笔试中很容易被一眼看穿所以难度不大。我的做题策略是先判断问题类型能暴力就先暴力再考虑优化。不要一上来就写最优解而是先保证思路正确、代码能跑逐步优化。在限时笔试中拿到部分分数比追求完美更重要这个心态很关键。6. 复盘与秋招准备建议6.1 时间安排与刷题策略如果你正在准备Android秋招笔试准备最好提前两个月开始。第一个月主攻基础Java集合与并发、Android四大组件、Handler、View体系每天固定两小时刷选择题重点整理错题。第二个月主攻算法和模拟每天1到2道LeetCode或牛客题每周至少做一次整套模拟笔试严格控制时间。刷题不要盲目追求数量要按模块来。数组、链表、字符串、二叉树、动态规划各刷30题左右基本能覆盖大多数笔试场景。笔试题的难度通常低于面试算法所以不需要花太多时间在hard题上反而要多练那些“容易写错边界条件”的中等题。建议至少做三套以上目标公司的往年笔试真题。这不是为了押题而是为了适应时间压力和题型分布。我当时花了几个周末在牛客上模拟真实笔试环境训练自己在90分钟内分配时间的节奏感这比单纯刷题效果更好。6.2 简历与笔试的联动准备笔试和面试其实是连贯的笔试里答错的题面试环节很可能被翻出来追问。所以每次笔试结束后我建议立刻复盘把每一道错题的知识点整理到笔记里写成“题目思路知识点”的结构。我当时有一个Electron笔记专门记录每场笔试的错题和反思。比如小鹏笔试结束后我发现自己对RecyclerView缓存层级的理解不够深就专门去读了源码中Recycler类的实现流程把四级缓存的调用时机彻底搞清楚。结果面试时真的被问到了相关细节当时就靠这份复盘答上来的。另外简历上写的东西一定要经得起笔试的延伸拷问。如果你的简历写了“使用过AIDL做进程间通信”笔试中出现AIDL相关简答题时尽量答得比简历描述更深入一点如果答得很浅反而暴露了经历的真实性。简历和笔试、面试是一个完整的证据链保持一致性非常重要。写在最后做完整套小鹏笔试我最深的感受是这套题不靠刷题库能应付过去它考验的其实是你平时写代码时有没有养成“向下看一层”的习惯。比如你知道Handler能更新UI但有没有想过它和Looper、MessageQueue是怎么配合的你每天用RecyclerView滑列表但有没有研究过它的四级缓存为什么这么设计。造车新势力的技术团队很清楚车载场景下的Android开发对稳定性和性能要求极高所以笔试筛选的不仅是“会写代码的人”更是“理解代码背后原理的人”。最后再分享一个准备小技巧每次笔试结束后不管过没过都花半小时把整张卷子复盘一遍。这个习惯能让你把面试官出题的偏好、考点分布、自己的知识盲区都看得清清楚楚。秋招是一场持久战每一份认真做的复盘都会在一次面试里用某种方式回报你。
返回列表