
2月底投的B站Android开发岗简历一周后收到一面电话邀约。整个流程从一面到二面结束大概十天最后没进HR面在二面环节被刷了。今天把它整理出来一是给自己做一次彻底的复盘二是希望帮准备大厂Android面试的朋友少踩几个坑。先把结论放在前面B站的面试强度不算变态不问你“红黑树怎么实现”“B树第几层放数据”这种八股深水区但它非常看重三样东西——原理深度、项目细节、表达逻辑。我基本栽在项目细节和表达逻辑上后面我会把每一道题、每个追问、我当时怎么答的、哪里出了问题都写清楚。如果你也在准备Android中大厂面试这篇值得看完。1. 面试流程复盘从投递到二面结束1.1 投递渠道与岗位信息我是在常规招聘App上投的岗位是Android开发工程师业务方向与视频社区相关。投递之后大概4个工作日收到了HR的电话简单确认了当前在职状态、可到岗时间、技术栈匹配度之后约了一面的时间。整个过程走的是远程视频面试一面面试官偏年轻看气场应该是组里的核心开发二面面试官明显更资深上来先做了一段自我介绍应该是部门技术负责人或者资深专家。提醒一句B站的招聘流程一般是技术一面-技术二面-HR面但我这次终止在二面所以严格说只走完了两轮。从一面和二面的问题分布来看一面考基本功和源码理解二面考项目深挖和设计能力两个环节的筛选逻辑完全不同。1.2 一面基础题覆盖面广追问答到为止一面整体时长大约70分钟前40分钟是技术问题中途有几道代码题最后10分钟留给我反问。面试官的风格是你先答答完了他会顺着你的答案往下追问但一般只追问两层。如果你在第二层就卡住他会标记并在下一题继续考察不会一直死磕一道题。这一点非常重要。后来复盘时我意识到一面面试官其实是在用“追问深度”来划分数线能解释清楚原理的算及格能结合源码说清实现细节的算优秀只能说出名词和结论的就会被打上“深度不足”的标签。我当时有几题就是第一层答得还算顺第二层直接露馅比如Kotlin协程的切线程原理、Handler的epoll唤醒机制都是我丢了分的关键点。1.3 二面节奏更慢每题都往死里挖二面时长约85分钟问题数量反而变少了但每个问题都被拆成无数个“为什么”。比如他让我介绍一个项目里的核心模块我讲了大概三分钟他后面连续问了七八个问题全部围绕这个模块展开为什么用这个架构、数据流怎么走的、出现OOM怎么验证、如果并发量再翻一倍你会怎么做、缓存策略为什么这么设计、不用这个库你自己实现行不行……二面的压迫感主要来自这里你没有机会“背题”因为所有问题都是从你嘴里说出去的内容里长出来的。你一旦说了一个方案就要准备好接受对方从性能、扩展性、健壮性、团队协作四个维度来审问它。我正好在几个细节上没有准备到位比如项目里一个存储方案的技术选型过程我只说了“因为项目简单所以选了它”这种回答在二面里基本等于送分题变成送命题。2. 一二面技术问题逐题回顾我按照记忆把有代表性的问题整理成了表格。带“丢分”标记的是我自认为回答深度不够或者方向有偏差的题目带“稳”标记的是答得还行的题目。2.1 一面基础题Java、Kotlin与Android基本功面试题我的回答状态考察点Kotlin协程与线程池相比有什么优势丢分协程本质、suspend原理、调度器Handler的Looper死循环为什么不会导致主线程卡死丢分MessageQueue与epoll机制说说Binder相对于其他IPC方案的优势稳Binder拷贝次数、安全性自定义View的measure、layout流程稳测量模式、ViewGroup测量过程ANR的触发条件和定位方法稳五大场景、 traces文件分析第一题Kotlin协程我当时的回答是“协程可以把异步代码写成同步风格方便做并发控制不会阻塞线程。”这个回答本身没错但太浅了。面试官接着问“那suspend关键字底层是怎么实现的withContext(Dispatchers.IO)为什么能把代码切到子线程再切回来”我卡住了。这里把正确的理解写一下对后面复习的同学有帮助协程本质上是编译器帮你写状态机。suspend函数被编译后会多出一个Continuation参数函数内部被改写成switchcase的形式每次挂起点对应一个case。切线程的原理则要看DispatcherwithContext触发dispatcher的dispatch操作把被挂起的Continuation放到指定线程的队列里去恢复执行。只答出“异步转同步”这层在B站这种级别的公司是不够的。第二题Handler的问题是面试高频中的高频。我当时答了“Looper.loop是死循环但没消息的时候MessageQueue会阻塞在nativePollOnce线程休眠不占CPU”但没有继续说底层是怎么实现的。面试官追问“它是怎么被唤醒的靠的是不是管道跟select、epoll有什么关系”我没答上来。正确思路是native层通过epoll机制监听一个eventfdMessageQueue没有消息时线程休眠在epoll_wait上其他线程通过往eventfd写入数据来唤醒它。Looper不卡死主线程是因为它在没消息时让CPU休息而ANR是因为主线程长时间处理不了输入事件两个概念不能混。2.2 一面源码题RecyclerView、Glide、OkHttp面试题我的回答状态考察点RecyclerView的缓存复用机制稳四级缓存、ViewHolder复用Glide加载一张大图为什么不会OOM丢分采样率、活动缓存、LruCache、DiskLruCacheOkHttp的ConnectionPool连接池工作原理丢分连接复用、清理策略、keepAliveRecyclerView这题我答得比较完整把四级缓存说清楚了Scrap缓存还在屏幕内但被标记remove的ViewHolder、CachedViews默认保存2个最近移出屏幕的ViewHolder、ViewCacheExtension自定义缓存实际用得少、RecyclerViewPool跨列表复用的共享池。还要补充说明的是ViewModel被缓存时不会重新执行bindViewHolder从Pool里取出才会重新绑定。Glide这题我只答了“内部做了图片压缩和缓存”太含糊。面试官追问“它加载一张2000x2000的图到100x100的ImageView图片内存要占多大具体是哪一步把它压缩的用了哪些内存缓存”我回答得比较乱。这里值得写清楚图片占内存的计算像素宽 × 像素高 × 每像素字节数。2000×2000的ARGB_8888要占约16MB直接被塞进内存很容易把App内存打爆。Glide的做法是先通过inJustDecodeBounds读取图片原始宽高再根据目标控件的尺寸计算inSampleSize做采样压缩。采样后的图片尺寸接近目标控件大小内存占用从16MB降到几十KB。缓存分为三层第一层是ActiveResources正在被引用的图片用弱引用维护、第二层是LruCache内存缓存大小默认由Glide根据App内存计算、第三层是DiskLruCache磁盘缓存原图、转换图、采样图三种版本。OkHttp连接池这里我当时只说了“有连接池默认5分钟复用一个连接”没有说清楚底层结构。正确的理解是连接池使用Deque存放RealConnection每条连接有两个字段记录最近一次使用时间。一个独立线程会遍历连接池发现某个连接的空闲时间超过keepAliveDuration默认5分钟就标记为淘汰并关闭。默认每个地址最多缓存5条连接复用逻辑发生在HTTP请求的route选择阶段。2.3 二面项目深挖架构、数据流与性能优化二面没有按套路出牌不问“你们项目用的什么架构”而是直接让我从零详细介绍一个自己主导过的技术模块然后全程围绕这个模块提问。我选择介绍的是一个内容社区App的帖子发布模块里面涉及图片选择、上传、草稿本地缓存、发布状态流转等功能。面试官问题清单大致如下这个模块的架构分层是怎样的每一层的职责是什么为什么这么分层图片上传是用的什么方案分片传还是整文件传为什么草稿数据存在哪里数据库还是文件表结构怎么设计的如果App被杀掉草稿还会在吗发布状态流转是怎么做的如果用户发帖过程中切换网络比如从WiFi切到5G这个流程会怎么处理怎么判断恢复有没有做过内存优化图片对象有没有泄漏你是怎么检测的这几个问题我大多能回答出来因为项目是自己做的大致思路都在。但存在的问题也明显表达不够结构化。面试官让我讲架构我绕来绕去说“用了MVVM”但没具体说ViewModel和Repository之间怎么交互、数据是单向下发还是双向绑定、状态是怎么收敛的。后来反思我对项目细节的理解还是停留在“我会用”的层面没有把它上升到“我为什么这么设计、怎么验证它是对的”这种工程决策层面。2.4 二面的场景设计题视频缓存与弱网策略B站本身是视频平台面试官出了一个视频场景的设计题手机上一个短视频App滑动播放时如何避免重复加载相同视频弱网环境下这个策略会怎么变化我的回答是先拆问题再给方案内存里用LRU缓存最近播放过的视频数据磁盘上用分片的方式缓存视频文件滑回来的时候先检查本地有没有对应分片有就直接从本地读。弱网环境下可以降低清晰度、预加载下一个视频的前几个分片。面试官追问了几个点我挑两个写一下因为很有代表性第一LRU是基于文件还是基于分片如果基于整个文件做LRU一个大的视频文件占满缓存后其他视频全被淘汰命中率很低。基于分片做LRU更合理视频文件按固定大小比如1MB切成多个分片冷门分片先淘汰热门视频的后续分片还能留在缓存里。第二为什么要做分片缓存它和普通文件缓存比优势在哪这个问题的答案其实和“边下边播”的实现有关。短视频App需要在播放到某个位置时就能立刻读取对应数据分片让播放器可以从文件随机位置读取数据不用等整个文件下载完。同时做预加载时也能按需加载例如下载前20秒对应的分片而不是整部视频。这题我整体答得还可以因为平时自己做过播放器相关的实验项目。但更大的问题暴露在下一轮面试官让我估算一个4G网络环境下分片缓存的带宽成本我完全没有方向。这说明设计题不仅考方案还考你对自己方案有量化的账单意识。3. 为什么凉了面试官考察点与我的失分项复盘3.1 失分点一原理能说出名词说不清机制这是我在一面里最明显的问题也是大多数Android开发准备面试时的通病。具体表现是知道“协程是线程的封装”“Handler有MessageQueue”“RecyclerView有四级缓存”但回答里始终有一个“窗户纸”没有被捅破。面试官追问两三次之后我的回答就变成了描述性语言而不是机制性解释。比如协程我说“不阻塞线程”但说不清阻塞和挂起的本质区别。挂起本质上是函数让出执行权但不占用锁和线程资源而线程阻塞是让线程停留在等待状态。协程挂起时线程可以去执行别的协程任务这背后是Dispatcher的调度。没有这层理解你的“不阻塞线程”就是一句听起来没毛病但没有分量的空话。建议准备面试时对每个高频知识点都问自己三个问题它解决了什么问题它是怎么实现的换一种方案为什么不行能把三句话用自己的话说明白并配上关键源码细节才算过关。3.2 失分点二项目细节经不起“三连追问”二面最大的教训是项目是你简历里最有话语权的东西但前提是你能把所有技术决策背后的权衡讲清楚。面试官不关心你项目的业务流程关心的是你做过的技术选型、遇到坑时的判断力、以及方案上线后的验证方法。我举一个我实际发生的反面例子项目里的草稿箱存储当时我用了Room数据库。面试官问“为什么不用SharedPreferences为什么不用文件存JSON”我的回答是“因为数据结构复杂数据库更方便”。这个答案有多单薄现在我回头看都很尴尬。他后续的追问我基本是在硬撑Room相比SQLite的优势是什么数据迁移你做过吗数据库损坏了怎么兜底把Room换成GreenDAO对你项目影响大吗正确的准备方式应该是什么对简历上的每一个技术点都要准备一个“选型矩阵”候选方案有哪些、每个方案的优缺点、为什么挑了这个、有没有替代方案在什么场景下更合适、当前的方案有什么坑。比如草稿存储除了Room你还要能说出SP的阻塞问题、JSON文件在复杂查询上的劣势、以及Room用注解生成代码带来的编译期校验优势。3.3 失分点三开放题思考过程混乱没有结构化表达二面的场景设计题我虽然给出了方案但在表达上很吃亏。面试官问“弱网环境下策略怎么变”我东说一句“降低清晰度”西说一句“增加超时时间”中间又跳到“失败重试”没有一个清晰的框架。后来我复盘碰到这种开放式设计题一个通用的回答结构是澄清需求先问清楚场景边界。比如“这里的弱网指带宽不足还是高延迟关注的是首帧时间还是卡顿率”拆解目标明确这个场景下最核心的优化指标是什么。弱网视频播放一般核心是减少首帧卡顿。列出候选方案预加载、分片缓存、码率自适应、重试策略等说清楚各自的代价。给出取舍和优先级优先做哪个、为什么什么条件下放弃哪个。量化评估预估效果比如缓存命中率提升多少、卡顿率下降多少。如果我当时用这个框架即使方案内容不变给面试官的信息密度和逻辑感也会完全不一样。面试官想看到的不是你瞬间给出完美答案是你面对开放问题时的结构化思考路径。3.4 面试官真正在考察什么把两个环节的问题放在一起看B站Android面试的考察点其实很聚焦第一你对自己写的代码有没有真正理解。所有追问都在验证一个事实你是这个模块的Owner还是只是个执行者。第二你对开源库和系统机制的理解有没有达到源码层。协程、Handler、Binder、RecyclerView、主流的三方库几乎是必问范围。第三你有没有工程判断力。如何做技术选型方案的成本是多少出了问题怎么排查这些问题的背后是工程师的成熟度。说白了面试筛的不是“会写代码的人”而是“能独立负责一个模块遇到问题能定位、能决策、能落地的人”。我一面挂了可能靠补基础还能过二面挂就挂在这层“工程判断力”的考察上这个只能靠实战和复盘慢慢补。4. 给准备这类面试的朋友一套扎实的Android准备路径4.1 原理类知识怎么复习才不“虚”很多人的八股文背得很熟但被追问就露馅问题出在复习方式上。我建议按“问题驱动手写简化源码”的方式准备不要直接背结论。以Handler为例不要只背“Looper死循环不卡”这个结论而是自己去把简化版流程写一遍MessageQueue的enqueueMessage排队逻辑、next()里什么时候会阻塞、阻塞在nativePollOnce哪个层级、主线程MessageQueue为空时会有什么行为、IdleHandler是什么时机触发的。用代码把每个节点串起来比背十遍“epoll”都管用。另外一个建议是学会给知识点画“1N”地图。以RecyclerView为例1是它的缓存复用机制N是它延伸出的性能优化要点为什么嵌套滑动会卡、setOnScrollListener做预加载和ItemDecoration冲突的坑、DiffUtil的算法原理、列表快速滑动的回收策略。面试官往往很喜欢从一个基础问题延伸到性能问题你准备得越深能接住的话题就越多。4.2 项目复盘要准备的“高频三连问”如果你的项目经历过严格复盘面试答项目题其实是最轻松的。我列一个自己整理的“项目审问清单”每轮面试前拿它对自己的项目过一遍这个模块为什么设计成这样架构分层是怎么考虑的相比另一个同样能实现功能的方案你凭什么选了这个有数据支撑吗数据流是怎么走的从UI层到数据层每一步发生了什么用了哪些开源库它的底层原理是什么如果它出现Bug你能从哪几个方向排查数据存储怎么设计的表结构是什么字段怎么建的索引数据大了之后怎么办遇到过的线上问题怎么发现的怎么定位的怎么修的修完怎么验证如果需求方告诉你这个功能要支持的数据量翻十倍你会动哪些地方这些问题如果不提前写一遍现场几乎不可能答好。我的教训是不要以为项目是自己搭的就没问题你脑中的项目是“骨架”写在文档里的、画成架构图里的才是“血肉”组织成语言表达出来的才是“灵魂”。4.3 场景题与设计题的答题框架除了技术原理这种中大厂技术面试几乎必考一道开放设计题。答题时最怕的是面试官已经把场景描述得很详细了你却还在等他给更多信息或者上来就噼里啪啦给方案。正确动作是先复述一遍需求确认我没有理解偏差。比如对方说“做一个图片加载库”你要确认核心指标是速度、体积还是兼容性。再约束边界支持哪些图片格式图片源是本地还是网络最大并发数多少然后按“整体架构-分层职责-核心流程-异常兜底-性能优化-上线验证”的顺序展开。过程中主动说取舍比如用LruCache还是DiskLruCache做内存缓存我会说LruCache主要负责内存层面的优先级淘汰DiskLruCache负责持久化两者结合的代价是缓存空间占用翻倍但命中率有明显提升。最后给一个“下一步验证方案”怎么埋点看缓存命中率、怎么对比优化前后的耗时。用这个框架答设计题就算方案不是最优面试官也能看到清晰的思路。我当时就是栽在“没有框架想到哪说到哪”希望你避开这个坑。4.4 针对B站这类内容社区App的岗位准备最后聊点针对性准备。B站的产品形态决定了它的Android技术栈有一定倾向性复盘之后我整理了几个方向供投类似的视频社区App岗位的同学参考视频播放链路播放器选型IjkPlayer/ExoPlayer/自研、起播速度优化、卡顿与丢帧监控、软硬解切换。这些是视频App的核心。弹幕系统弹幕渲染方案、高并发弹幕的消息分发、弹幕遮挡与透明度处理。Android自定义View在这个场景下是高频考察点。页面性能首页Feed流、评论区这种高频长列表的流畅度优化、布局层级优化、启动耗时治理。缓存与弱网视频分片缓存、缓存淘汰策略、弱网下的清晰度切换、预加载时机的选择。内容安全涉黄涉政内容的识别策略、上报与审核链路、评论/私聊的敏感词过滤等。这算是内容社区岗位的特色问题也会被考察。如果你能提前准备一个和自己投递业务方向强相关的小项目或者在简历的项目经历里体现你在这些方向上的思考面试官会明显更有兴趣往下聊。我这次没走到HR面自然谈不上Offer但复盘之后反而觉得暴露问题是好事。面试的意义不只是拿Offer也是用两小时帮你扫盲。上面这些都是我这次面试中实打实的体会。最后分享一个我踩坑后调整的复习方法准备面试时别只刷面经要把你手头正在做的项目按“3.2节那个审问清单”过一遍能写下来的全部写下来逼着自己去把每个细节说清楚。写下来的过程很痛苦但基本写一遍就能覆盖80%的高频追问。B站愿意给面试机会说明简历是过关了剩下的事就是自己怎么把掌握的东西清晰、有逻辑地交付出去。祝你能比我走得更远。