每年秋招季,Android方向的笔试题都牵动着大量应届生的神经。今天想和大家聊聊哔哩哔哩2020校园招聘Android方向笔试卷(一),这份试卷我前后带过不少学弟学妹复盘,也帮几个拿到B站Offer的候选人做过模拟训练,对它的出题风格和考察逻辑算是比较熟悉。如果你正在准备Android岗位的校招笔试,或者只是想检验一下自己的Android基础是否扎实,这篇内容应该能给你提供一份比较完整的参考。
这份试卷最值得关注的地方,在于它不是单纯的“背概念”型题目,而是把Android开发中的基础原理、常见机制和实际工程问题揉在一起考。我从出题人视角、知识点拆解、典型题目实战、以及笔试现场容易踩的坑这几个维度来展开,尽量还原一份真实的备考路径。
1. 试卷整体设计与出题思路拆解
1.1 B站Android校招笔试到底在考什么
先给没接触过B站校招题的同学一个整体印象。一般互联网公司的Android笔试卷,主要由四个部分构成:计算机基础(数据结构、算法、操作系统、网络)、Java/Kotlin语言基础、Android核心机制、以及一到两道综合性设计题或编程题。B站2020校招这张卷子也是沿着这个框架展开的,但在Android部分的比重明显偏高,大约能占到50%以上。
从实际反馈来看,这份试卷的重点集中在以下几个方面:
- Java基础与并发编程,比如HashMap原理、线程池参数、volatile和synchronized的区别。
- Android四大组件的工作流程,尤其是Activity启动模式、Service绑定流程、BroadcastReceiver的注册方式。
- Handler消息机制,这个几乎是每张Android笔试卷的必考题。
- 自定义View的测量、布局、绘制流程。
- 性能优化相关,包括内存泄漏、ANR、布局优化。
- 网络请求与数据持久化方案。
这个考察结构其实挺有讲究的。B站作为一个视频社区App,对流畅度、播放体验、内存占用要求都很高,所以他们在校招笔试里特别看重候选人对Android系统机制的理解深度和性能优化的意识。如果你只是会写界面、会调接口,没有深入理解过底层原理,在这张卷子上会明显感觉吃力。
1.2 为什么这样设计——从出题人视角看考察逻辑
站在出题人的角度,校招笔试的筛选目标不是招“什么都会的人”,而是招“基础扎实、有潜力、能快速上手的人”。所以你会看到这份试卷里的题目,很少有偏门怪题,大部分都是Android开发中最常用、最核心的知识点。但“常用”不等于“简单”,出题人会用各种方式考察你是否真正理解,而不是仅仅记住结论。
举个例子,Handler相关题目不会直接问“Handler的作用是什么”,而是会结合Looper、MessageQueue、ThreadLocal来问,比如“一个线程可以有几个Looper”“子线程中能不能直接new Handler”。这种问法就是在考察你是否理解Handler的整体工作机制,而不是停留在“Handler用于线程间通信”这个表面答案上。
再比如自定义View题目,通常会给你一个具体的需求场景,让你描述onMeasure、onLayout、onDraw的调用时机和各自职责。这类题目考察的是你是否真的写过自定义View,是否踩过测量的坑,是否知道MeasureSpec的三种模式到底是什么意思。在我看来,B站这张卷子的出题质量是比较高的,它不靠题量堆人,而是靠题目深度筛选人。
2. 核心知识点拆解与答题要点
2.1 Java基础与并发:校招笔试的“送分题”与“送命题”
Java基础部分在试卷里通常是第一板块,看起来是最简单的,但恰恰是很多人丢分的重灾区。高频考点包括HashMap的底层实现、ArrayList和LinkedList的区别、==和equals的区别、String/StringBuilder/StringBuffer的区别、异常体系、泛型擦除等等。这些题目只要认真准备过,基本都能答上来,属于“送分题”。但如果你只是背了答案,没有真正理解原理,一旦出题人换个角度问,就很容易露馅。
比如HashMap这个点,B站这张卷子比较喜欢考的是“HashMap在JDK 7和JDK 8之间有什么变化”。很多同学能说出“JDK 8引入了红黑树”,但再往下问“为什么链表长度超过8才转红黑树”“为什么红黑树的阈值是8而不是16”,就卡住了。这里我建议答题时往两个方向展开:第一,哈希碰撞导致链表过长时,查询效率从O(1)退化到O(n),用红黑树可以把最坏情况优化到O(log n);第二,选择8作为阈值,是根据泊松分布计算的,在负载因子0.75、哈希函数分布均匀的前提下,链表长度达到8的概率已经极低,所以这个阈值是时间和空间上的折中。
并发编程也是一个重点方向。线程池的七大参数、volatile的可见性和禁止重排、synchronized的锁升级过程、CAS的ABA问题,这些都是高频考点。答并发题的时候,我建议一定要画图辅助说明。比如讲synchronized锁升级,可以画出无锁→偏向锁→轻量级锁→重量级锁的路径,再解释每种锁的适用场景和Mark Word里的标记位变化。这种方式不仅让答案更有条理,也能向阅卷人展示你对底层原理的理解深度。
2.2 Android四大组件与Handler机制:从“会用”到“懂原理”
四大组件这部分,Activity是考察最多的。启动模式(standard、singleTop、singleTask、singleInstance)几乎是必考的,但出题人很少直接让你罗列四种模式,而是给场景让你判断。比如“App从通知栏点击跳转到某个页面,希望这个页面在栈中只存在一个实例,应该用什么启动模式”,这种考法需要你理解每种模式对Task栈的影响,而不是单纯背概念。
我在这里列一个简表,方便你复习时对照:
| 启动模式 | 行为特征 | 典型使用场景 |
|---|---|---|
| standard | 每次启动都创建新实例,放入启动它的Task | 普通页面跳转 |
| singleTop | 栈顶复用,否则新建 | 通知栏点击跳转 |
| singleTask | 栈内复用,并清除其上所有Activity | App主页 |
| singleInstance | 单独占一个Task,全局唯一 | 来电界面、播放器界面 |
Service这块,重点理解startService和bindService两种启动方式的区别,以及它们的生命周期回调顺序。常见坑点是“Service在子线程中能不能做耗时操作”——答案是不能,Service默认运行在主线程,耗时操作需要自己开线程或者用IntentService。B站这张卷子还特别喜欢问“Service和Activity通信的方式有哪些”,我建议至少答出三种:Binder、BroadcastReceiver、回调接口,如果能把LiveData和ViewModel的通信方式也提一下,会显得你技术视野更开阔。
Handler机制是重中之重。它考察的不仅是Handler本身,还涉及Looper、MessageQueue、ThreadLocal、内存泄漏、同步屏障、IdleHandler等一系列关联知识点。答题时可以按这样的脉络展开:先讲Handler的四个核心对象(Handler、Looper、MessageQueue、Message)各自的职责;再讲它们之间的协作流程——Handler通过sendMessage把Message放入MessageQueue,Looper通过loop()方法不断从队列中取出Message,再dispatchMessage给Handler处理;然后补充ThreadLocal的作用,说明每个线程通过ThreadLocal持有唯一的Looper实例;最后一定要提主线程Looper的启动时机,也就是ActivityThread的main方法里调用了Looper.prepareMainLooper()。
2.3 性能优化与内存管理:体现工程能力的关键板块
性能优化在B站2020校招笔试试卷中的比重不低,这也反映了他们对客户端体验的重视。内存泄漏是必考内容,最典型的场景包括:Handler持有Activity的引用导致Activity无法回收、静态Context引用、单例持有Activity、匿名内部类持有外部类引用、注册的监听器没有反注册、资源没有关闭。答题的时候,除了列出泄漏场景,更重要的是给出解决方案,比如Handler用静态内部类加弱引用、销毁时移除回调和消息、用ApplicationContext代替ActivityContext。
ANR也是高频考点。你需要知道ANR触发的三种场景:输入事件5秒无响应、BroadcastReceiver前台10秒/后台60秒未执行完成、Service前台20秒/后台200秒未执行完成。排查思路通常是先用adb命令导出ANR日志,再结合logcat中的ANR in关键字定位发生ANR的进程和组件,最后用trace文件分析主线程的调用栈,找到卡顿的函数。
布局优化这块,要注意include、merge、ViewStub三种标签的使用场景区别。很多人知道include用于复用布局、ViewStub用于延迟加载,但不清楚merge到底解决什么问题。我建议这样回答:merge主要用于减少不必要的层级嵌套,比如一个LinearLayout里面只有一个子布局需要引用时,直接用merge作为根标签,可以让子布局直接并入父容器,减少一层View树的深度。B站这类大流量的App对渲染性能非常敏感,把这个点答清楚会很有加分效果。
2.4 网络、数据持久化与第三方框架:考察你的实战积累
网络这块主要考HTTP/HTTPS的基本知识,比如HTTP的请求方法、状态码含义、HTTPS的握手过程,以及OkHttp的拦截器链。B站笔试比较喜欢结合播放业务来问,比如“视频播放时网络波动,如何做断点续传”,这种题目其实是在考察你对HTTP Range请求头、OkHttp自定义拦截器、本地缓存策略的综合运用能力。
数据持久化的考察点包括SharedPreferences、SQLite、Room、MMKV、DataStore。常见的问法是“SharedPreferences有什么优缺点,为什么不适合存大量数据”。这个问题要答到点子上:SP加载是异步的,但第一次访问时如果文件没有加载完,会触发阻塞等待;SP是全量写入,每次apply都会把整个文件写入内存再异步落盘,数据量大时会有明显卡顿;SP不支持多进程安全,即使加了flags也会存在数据丢失风险。
第三方框架方面,EventBus、RxJava、Glide、Retrofit都有可能出现在笔试卷上。以Glide为例,考点集中在图片加载流程、三级缓存、生命周期绑定。回答时建议从into()方法入手,讲清楚它如何根据ImageView尺寸解析RequestBuilder,如何通过Engine加载缓存或发起请求,以及如何通过RequestManagerFragment监听生命周期并暂停/恢复加载任务。框架类问题不要只答用法,要落到源码机制上,这样才显得你有源码阅读的习惯。
3. 典型题目实战演练与答题框架
3.1 高频简答题的答题模板:从“会做”到“会写”
笔试和面试不太一样,面试是口头表达,笔试需要把答案写清楚。很多技术不错的学生在笔试时栽跟头,不是因为不会,而是因为写出来的答案逻辑混乱、抓不住重点。我建议简答题采用“先结论、后原因、再展开、最后举例”的四步模板。
举个例子,如果题目是“简述Handler机制的原理”,可以这样组织:
第一步给出结论:Handler机制是实现Android主线程与子线程通信的核心框架,由Handler、Looper、MessageQueue、Message四个对象协作完成。
第二步说明原因:子线程不能直接更新UI,Android的UI访问只能在主线程进行,所以需要一种机制把子线程的消息传递到主线程执行。
第三步展开原理:主线程启动时会通过Looper.prepareMainLooper()创建Looper,Looper内部持有MessageQueue;子线程通过handler.sendMessage()往MessageQueue中插入消息;Looper.loop()进入死循环,不断从MessageQueue中取出消息,通过handler.dispatchMessage()回到主线程处理。ThreadLocal保证每个线程有且仅有一个Looper。
第四步举例说明:比如子线程下载图片后需要更新ImageView,就通过handler.post()将更新操作post到主线程队列中,主线程Looper取到消息后执行更新。
这套模板的好处是层次分明,阅卷人扫一眼就能看到你的答案结构,也会觉得你思路清晰。我每次带学生做笔试模拟,都会让大家按照这个模板练习,实操下来效果比自由发散式答题好很多。
3.2 一道自定义View题目的完整推导:测量、布局、绘制全流程
自定义View是B站这类视频类App笔试的高概率考点,因为视频播放页、弹幕层、手势控制层都有大量自绘需求。这类题目通常不会让你写完整代码,而是让你描述一个自定义View从创建到显示的全过程。假设考题是这样的:“自定义一个圆环进度条,请描述实现思路,包括测量、布局、绘制三个环节。”
测量环节,核心是onMeasure方法。你需要说明MeasureSpec的三种模式——UNSPECIFIED、EXACTLY、AT_MOST,以及它们分别对应什么场景。对于圆环进度条,宽度和高度通常是确定的,所以可以给一个默认尺寸,比如wrap_content时给一个100dp的默认值,match_parent时就直接用父容器给的MeasureSpec尺寸。注意,自定义View在wrap_content时如果不重写onMeasure,会直接使用父容器传入的MeasureSpec,效果等同于match_parent,这是很多人踩过的坑。
布局环节,onLayout在ViewGroup中才需要重写,单个View不需要。如果你的圆环进度条是一个独立的View,直接跳过onLayout即可。但如果你要做一个带子View的组合控件,就需要在onLayout中遍历child,确定每个子View的位置。
绘制环节,这是重点。onDraw里会拿到Canvas,核心操作是画背景圆弧和画进度圆弧。画进度圆弧时需要用到Paint的setStrokeWidth设置笔触宽度、setStrokeCap设置为ROUND让端点是圆角、setStyle设置为STROKE画空心圆。注意,绘制进度时通常用RectF来确定圆弧的外接矩形,startAngle和sweepAngle分别表示起始角度和扫过的角度。进度条的动画效果可以通过ValueAnimator监听动画值变化,再调用postInvalidate()刷新视图。
最后一定要提一下invalidate和postInvalidate的区别:invalidate只能在主线程调用,postInvalidate可以在子线程调用,它的内部会通过ViewRootImpl切换到主线程执行重绘。这道题如果能把这个细节也补上,能体现你对View刷新机制的完整理解。
3.3 算法与编程题准备:不要轻视这一板块
Android笔试的算法题通常不会太变态,但也不至于简单到随便写写就过。常见的题型包括:数组去重、链表反转、二叉树遍历、字符串匹配、动态规划入门。B站2020校招这张卷子的编程题,从同学们反馈来看,倾向于考一些和数据结构强相关的中等难度题。
备考建议是按专题刷题,不要盲目刷量。我比较推荐把精力集中在以下几类:双指针(快慢指针、左右指针)、哈希表辅助、链表操作、二叉树前中后序遍历、简单的动态规划(爬楼梯、最大子序和)。每类题至少刷10道,做到能默写核心模板的程度。
笔试时写代码,注意三点:第一,先写注释理清思路再写代码;第二,考虑边界条件,比如链表为null、数组长度为0、输入为负数;第三,用好语言特性,比如Java的PriorityQueue、Arrays.sort、HashMap,能省不少时间。
还要强调一点:编程题不要只提交代码就完事,如果有时间,在代码旁边用注释写出你的时间复杂度和空间复杂度。阅卷人看到你会分析复杂度,印象分会明显不一样。
4. 笔试实战中的常见问题与避坑技巧
4.1 时间分配与做题顺序
一套校招笔试的时间通常是一个半小时到两个小时,题量在20到30题之间。我见过很多学生因为时间分配不当,前面选择题纠结太久,后面大题没时间写。我的建议是拿到试卷后先快速浏览一遍所有题目,标记出会做的、拿不准的、完全不会的,然后按“先易后难、先分高后分低”的顺序作答。
具体来说,先把有把握的概念题和送分题解决掉,保证基本盘;然后做Android核心机制题,这类题分值高且你有发挥空间;最后剩时间再做算法编程题。编程题如果需要长时间调试,宁可在纸上写好伪代码再往上誊,也要保证其他题目的正确率。
我遇到过不少学生,在一道两三分的选择题上花十分钟,结果后面一道十五分的简答题只写了两行字。这种丢分真的很可惜。建议大家平时做模拟卷时就有意识地计时练习,培养时间感。
4.2 容易丢分的细节清单
根据我帮学生们复盘的经验,B站2020校招Android笔试卷上有几个很容易丢分的细节,值得单独拎出来提醒:
- 题目要求用Kotlin写代码时,不要提交纯Java代码,反之亦然。先看清编程题的语言要求。
- 简答题涉及多个知识点时,漏掉其中一个就会被扣分。比如问“Activity启动过程”,至少需要讲清楚AMS、Instrumentation、ActivityThread、ViewRootImpl这几个角色的协作。
- 涉及流程的题目,一定要画流程图。纯文字描述容易冗长且遗漏关键步骤,一张清晰的时序图能帮你省掉很多解释。
- 手写代码时注意语法正确性,尤其是分号、括号、import语句。阅卷人看的是代码逻辑,但语法错误过多会降低整体评价。
- 注意单位。比如内存大小、时间阈值、dp和px的换算,写错一个零可能全错。
这里我特别想说一下流程图的重要性。笔试是踩点给分,文字描述中的关键步骤容易被淹没,但图不一样,一眼就能看到你的流程是否完整。好多同学觉得画图浪费时间,其实一张图二三十秒就能画完,收益却很高。
4.3 复盘方法与后续面试衔接
笔试结束后,不管发挥如何,一定要做复盘。我个人的习惯是考完当天趁着记忆还清晰,把每道题考察的知识点列出来,然后对照自己的答案找出失分点。不要只看对错,要分析是“完全不会”还是“会但没答好”,这两种情况后续的复习策略完全不同。
如果已经收到了面试通知,笔试复盘就更加重要,因为面试官大概率会围绕笔试中的薄弱点进行追问。比如笔试卷子里有一道Handler的题你没答完整,面试时几乎肯定会被要求“详细讲一下Handler机制”。这时候如果你还不去补齐,就等于在面试中暴露了同一个短板。
关于后续复习方向,我建议按这样的优先级来安排:先把基础知识过一遍,重点掌握上面提到的Java并发、Handler机制、四大组件、自定义View;然后再去读几个主流框架的源码,以Glide、OkHttp、EventBus为起点,养成阅读源码的习惯;最后保持每天2-3道算法题的刷题节奏,直到拿到Offer。
第四个板块有点特殊,我想聊聊笔试之外的软因素。B站这张卷子的难度,客观来说在互联网大厂中算是中上水平,但不是那种故意刁难人的难度。它更看重的是你是否具备“工程思维”——拿到一个问题,能不能拆解成模块,能不能考虑边界条件,能不能把底层原理讲清楚。这种能力不是考前突击能练出来的,而是要靠平时写代码时多想一层、多问一个为什么来积累。
如果你还在备考阶段,我给你一个建议:把每一次笔试都当作一次学习机会,而不是一次单纯的筛选。做错的题、没答上的知识点,都是你后续提升的方向。我自己刚入行时也经历过很多次笔试失利,但正是那些失利逼着我把基础打扎实,后来才慢慢有了带别人复盘的能力。
最后再分享一个小细节,B站这类业务型公司比较看重候选人对产品的理解。笔试结束后如果有机会,可以了解一下B站App的功能特点,比如弹幕系统、视频播放、社区互动、直播功能,想一想这些场景背后用到了哪些Android技术。这样在后续面试聊项目时,你会更容易和面试官产生共鸣。希望这份试卷拆解能帮到你,也祝你备考顺利。