公司动态

商汤科技Android校招笔试复盘:从基础到AI落地的分水岭

📅 2026/8/31 20:02:12
商汤科技Android校招笔试复盘:从基础到AI落地的分水岭
2018年那会儿商汤科技的校招笔试在技术圈里热度很高。身边不少同学在牛客网刷完题出来第一句话就是“这哪是考Android这是在考计算机基础加业务设计。”我当时也被这套题折磨得不轻但事后回头看这场笔试的命题思路放在今天依然有很强的参考价值。尤其是对想进AI公司做客户端开发的同学来说商汤这套题基本划出了“AI公司Android工程师”和“普通App工程师”之间的那道分水岭。这篇文章我打算从卷面结构、典型题目复盘、备考路线、隐形考点几个维度展开不会只给你列知识点清单而是尽量还原当时答题的思路和踩坑过程。无论你是正在准备校招的应届生还是工作几年想跳槽到AI赛道的Android开发这篇复盘应该都能让你少走一些弯路。1. 这场笔试的定位AI公司招客户端到底在筛选什么人先说一个很多人容易误解的点商汤不是互联网App公司它是一家以计算机视觉和深度学习为核心的技术公司。但它又有大量业务需要落地到手机端比如人脸识别SDK、AR特效、智能相册、安防巡检App这些都需要Android工程师来做。所以它的Android岗位笔试不会只盯着你“会不会写界面”而是要看你“能不能在资源受限的移动端做好算法落地”。1.1 为什么商汤的Android笔试值得单独复盘市面上关于Android校招笔试的帖子很多但绝大部分来自美团、字节、百度这种以业务功能为核心的互联网公司考察重点往往是网络库封装、组件化架构、性能优化、大型项目协作。商汤这类AI公司的笔试风格完全不同它会直接把你往“底层原理 算法应用 系统机制”的方向牵引。举个例子当时卷子里有一道跟图片处理相关的题表面是问Bitmap的内存计算实际是在考你有没有做过图像类App的内存治理。你在普通App里可能只需要load一张网络图但在商汤的业务里你可能会连续处理几十帧相机流内存稍不注意就OOM。这种业务背景决定了它的笔试题目会比一般互联网公司更偏向底层和计算。1.2 “第二场”说明了一个现实问题商汤笔试是批量筛选机制标题里的“第二场”也值得琢磨一下。商汤校招不像小公司那样只有一场笔试它会在不同城市、不同批次安排多场。第二场一般是第一批笔试后的补录场次或平行场覆盖的是没有赶上第一批投递、或者第一批笔试结果不理想的候选人。我当时的感受是第二场的题目难度和第一场基本持平但会在某些题目上做细微调整比如换一组启动模式的场景、换一个自定义View的测量案例。这说明它有一套比较成熟的题库体系题目之间是有等价的考察逻辑的你刷题不能靠背原题而要理解背后的考察点。凡是以为“我把第一场原题背下来就能过第二场”的人基本都吃了大亏。2. 卷面结构拆解从Java基础到AI入门每个模块都在筛不同能力整张卷子大概持续90到120分钟题量不小题型跨度也大。我现在回忆起来大致可以分成六个模块每个模块对应一种能力的筛选。2.1 Java与语言基础不是背概念是看你会不会“用”第一部分的题目看起来比较基础比如String、StringBuilder、StringBuffer的区别HashMap的扩容机制Java内存模型里的可见性。但商汤的出题方式很鸡贼它不会直接问你“HashMap默认负载因子是多少”而是给你一段多线程环境下HashMap扩容死循环的代码让你分析问题出在哪。这种考法更深一层的意思是看你对Java集合类源码有没有真正读过而不是停留在八股文背诵层面。我当时复习的时候把HashMap的put、resize、treeifyBin这些方法源码都过了一遍所以看到这种题还能答上来。另外一个高频考点是Java的引用类型特别是WeakReference和ReferenceQueue。这个知识点在普通Android面试里也常出现但在商汤的笔试里它有了实际场景图片缓存、Handler防止内存泄漏、Bitmap回收。你得能说清楚为什么用WeakReference包裹Activity或者Runnable能解决内存泄漏以及什么时候该用SoftReference而不是WeakReference。2.2 Android四大组件启动模式不是背表是解决实际冲突Android的四大组件部分Activity启动模式绝对是必考题。但商汤的考法会结合具体场景比如在一个聊天应用里你从通知栏点击跳转会话页面如果当前栈里已经有这个会话页你应该用哪种启动模式如果连续收到多条消息通知你怎么避免栈里堆积多个相同页面这个问题考察的其实就是singleTop和singleTask的区别。我在答题时专门强调了一个细节singleTop只在栈顶复用如果目标Activity不在栈顶它依然会新建实例而singleTask会清空目标Activity之上的所有Activity。聊天应用这种场景直接用singleTask可能不太合适因为它会把用户正在浏览的其他页面全部清掉所以实际工程里往往采用singleTop配合Intent的FLAG_ACTIVITY_CLEAR_TOP来组合实现。Service、ContentProvider、BroadcastReceiver也有涉及但占比没有Activity那么高。我记得有一道关于ContentProvider的题是问你如何跨进程共享一张图片这道题很多人第一反应是传Bitmap但正确的思路应该是用ContentProvider返回一个Uri本质上仍然是Binder通道上的文件描述符传递。如果你没有真正做过跨进程通信这个点很容易答偏。2.3 自定义View与事件分发这里拉开的差距最大自定义View在商汤笔试里占据的比重非常高因为它们的产品需要大量自绘UI人脸框、手势识别指示器、美颜滑杆、滤镜调节面板等等。卷子里直接给出了一个自定义View的onMeasure代码片段让你分析当父布局给的是EXACTLY模式而子View的widthMeasureSpec是AT_MOST时最终尺寸是怎么确定的。这道题表面考MeasureSpec实际考的是你对View测量流程的理解程度。我当时写答案时把三种测量模式整理成了一个表格MeasureSpec模式父布局意图子View可决定空间常见场景EXACTLY父布局已确定精确尺寸无必须遵循match_parent或具体dp值AT_MOST父布局给出最大上限可以取0到上限之间任意值wrap_contentUNSPECIFIED父布局不做限制完全自由ScrollView、RecyclerView测量很多人在第一轮刷题时把这三个模式背得滚瓜烂熟但到做题时就忘了最重要的一句话MeasureSpec是由父布局的MeasureSpec和子View的LayoutParams共同决定的子View在onMeasure里拿到的MeasureSpec是父布局通过getChildMeasureSpec计算后传给它的。如果你只记住模式含义没理解这条传递链路后面遇到复杂布局的测量问题就很容易翻车。事件分发也是重头戏。它给出一个RecyclerView嵌套横向滑动卡片的场景问你如何保证纵向滑动时列表能正常响应横向滑动时卡片能自己处理。这不是靠背dispatchTouchEvent、onInterceptTouchEvent的返回值就能答好的。你必须清楚事件冲突解决的核心是判断“谁对这次滑动更有意图”然后在父布局的onInterceptTouchEvent里根据滑动角度、速度、方向去决定是否拦截。我在答案里写了按滑动距离差值的绝对值来区分横竖方向同时考虑touchSlop来避免手指轻微抖动造成的误判这种细节就是拿分点。2.4 并发、内存与性能移动端算法落地的基本功性能优化这一块商汤的题特别贴近实际业务。比如它问在低端机上运行实时人脸检测每帧图像是1280x720检测耗时30ms渲染耗时10ms但界面还是掉帧你最可能从哪里排查这一题放在普通App里大家可能先想到布局层级、过度绘制。但在这个场景里布局根本就不是瓶颈真正的瓶颈大概率是CPU和GPU之间的数据拷贝、线程调度优先级、或者相机帧数据格式转换。当时我给出的排查顺序是先用Systrace看掉帧发生在哪个阶段再检查是否主线程在wait/sleep然后用adb shell top看CPU占用分布最后才考虑绘制层面的优化。这个排查链路不是书上看来的而是平时做性能分析时踩了无数坑才形成的经验。内存泄漏也是必考。除了Handler、静态变量持有Activity、匿名内部类这些常规考点商汤还考了一个特别有区分度的点Camera的预览回调里持有ImageReader的引用为什么会导致内存泄漏这道题的坑在于很多人知道要在onPause里close()但说不清楚泄漏的链路。实际上ImageReader如果没被close底层BufferQueue会一直持有Surface的引用Surface又关联到Activity的SurfaceControl链路就断不掉了。这种题不会出现在常规的面试题集里但它真的是做Camera相关业务时必须面对的问题。2.5 网络与存储客户端的基本素养但考得比你以为的深网络这一块不是简单的“HTTP和HTTPS的区别”而是给出了一个实际场景在弱网环境下上传一段视频你会怎么设计上传方案。我当时写的是分片上传 断点续传 指数退避重试 幂等校验每一层都解释了原因。分片是为了避免单次请求体过大导致超时断点续传是为了用户切后台再回来不用重新上传指数退避是为了避免弱网下大量请求重试把网络打崩幂等校验是为了防止服务端重复写入。存储这一块出现了Android 6.0运行时权限和文件目录分区的题问的是应用在运行时请求存储权限后你写的文件到底应该放在哪个目录。很多人会直接说Environment.getExternalStorageDirectory()但在分区存储和Scoped Storage的时代这个API已经被限制得很厉害。正确的做法是优先使用Context.getExternalFilesDir()这样既不需要申请权限也不会污染公共目录卸载应用时系统也会帮你清理掉。2.6 AI基础商汤特色的“加试题”但不算超纲既然笔试的是AI公司的Android岗卷子里必然会出现一些AI相关的基础题。我当时做的那场卷子里有一道题是让写一个简单的图像灰度化或二值化算法伪代码分析时间复杂度和优化空间。这道题对纯客户端背景的人来说可能有点慌但你在商汤做Android哪怕是做UI层也免不了要跟图像数据打交道所以它考的不是深度学习理论而是最基础的数字图像处理思维。我当时的答题策略是先写最直观的双循环逐像素处理然后把复杂度写出来再给出一个用查表法优化灰度映射的思路。这样一来即使我的算法不是最高效的考官也能看到我有优化意识。这种题目不指望你写出CUDA级别的并行代码它要的是你具备“处理像素数据”的基本敏感度。如果你实在不懂AI基础也不用太慌它占的分值不会很高而且通常跟Android的Bitmap/Canvas知识绑定在一起你只要把Bitmap的基本操作搞明白再补一点点图像处理常识就够了。3. 几道让我印象深刻的题目与答题思路复盘笔试的坑踩过之后我最大的体会是光知道知识点不够你得知道怎么把知识点组织成一套“面试官爱看”的答案。下面挑几道我印象深刻的题说说是怎么一步步拆解回答的。3.1 一道Handler机制全链路题题目给了一段代码在子线程里new Handler并调用sendMessage然后问会出现什么结果。很多人第一眼会想起“子线程里不能new Handler因为Looper还没prepare”这个经典结论。但如果仅仅答这一步只能拿一半分。真正完整的回答应该是先看子线程有没有主动调用Looper.prepare()如果没有那么new Handler时会在Handler的构造函数里调用Looper.myLooper()拿到的结果是null于是抛出RuntimeException。如果已经调用Looper.prepare()那么子线程可以拥有自己的Looper和MessageQueue但如果你没有调用Looper.loop()Message虽然能被enqueue到MessageQueue里却永远不会被取出来分发因为loop()才是真正驱动消息循环的引擎。这道题我之所以印象深刻是因为它把“子线程能不能new Handler”从一道判断题变成了一道分析题。你在面试里如果能把“prepare负责创建Looperloop负责驱动循环Handler负责发送和处理消息”这条链路讲清楚绝对能跟只会背结论的候选人拉开差距。3.2 一道事件分发场景题谁会吃掉这次点击这道题给了一个非常真实的界面一个全屏的FrameLayout里面放了一个ButtonButton设置了OnClickListenerFrameLayout设置了OnTouchListener然后触摸Button之后问事件会怎么流转。当时我画的流程是这样的事件从Activity.dispatchTouchEvent进入经过PhoneWindow的DecorView一路上传到ViewGroup的dispatchTouchEvent。FrameLayout作为ViewGroup先判断自己是否要拦截如果它的onInterceptTouchEvent返回false则把事件分发给子View Button。Button在dispatchTouchEvent里先判断有没有OnTouchListener如果有且返回true那么OnClickListener不会被触发。如果OnTouchListener返回false则调用onTouchEvent在ACTION_UP时触发OnClickListener。如果Button没有消费事件事件会回传到FrameLayout的onTouchEventFrameLayout没处理再上传到Activity的onTouchEvent。这个题很容易答漏的一环就是OnTouchListener和onTouchEvent的优先级关系。很多人知道OnTouchListener先于onClick执行但不知道它是在dispatchTouchEvent阶段被调用的而且它的返回值直接决定后续onTouchEvent还要不要执行。这道题到了这里还没结束它还追问了一句如果给这个Button设置了一个很复杂的触摸水波纹效果你会不会考虑把事件处理的逻辑放到自定义ViewGroup里去统一调度这其实是在考你有没有做过高性能触摸交互优化而不只是停留在“背一遍分发流程”的阶段。3.3 一道自定义View测量题wrap_content为什么会失效这道题的题干是自定义了一个View没有重写onMeasure在XML里设置了宽度为wrap_content结果这个View的宽度变成了match_parent问为什么。很多人看到这个题立刻想到自定义View如果不重写onMeasure那么默认的测量行为就是遵循父布局的MeasureSpec而wrap_content在View的默认实现里会被当成AT_MOST模式下的match_parent来处理。因为View的默认onMeasure逻辑是getSuggestedMinimumWidth()和MeasureSpec.getSize(widthMeasureSpec)取一个值在AT_MOST模式下getSize返回的是父布局给的最大可用宽度所以实际宽度就等于父布局的宽。这个题背后的工程教训是任何自定义View只要你打算支持wrap_content就必须重写onMeasure并且在onMeasure里主动解析MeasureSpec给wrap_content设置一个合理的默认尺寸比如文字换行后的测量宽度或者图片的原始宽度。我在实际项目里还遇到过更隐蔽的情况自定义ViewGroup如果不处理child的LayoutParams也会出现类似问题而且排查起来更困难。3.4 一道开放题如何设计一个扫码页面这不是传统意义上的单选多选而是给了一段业务描述让你设计技术方案。题目大意是要在一个Android应用里实现扫码功能扫码流程包括相机预览、图像采集、条码识别、结果回调请描述你的技术方案并说出关键性能指标。这道题我答了大概两页A4纸的量核心设计如下使用Camera2 API而不是Camera1因为Camera2支持按帧控制曝光、对焦区域和输出尺寸对条码识别的适应性更好。相机预览的Surface选择上先用ImageReader拿YUV_420_888格式的帧再转成NV21或者直接送给底层识别库避免经过Bitmap转换带来的额外内存拷贝。识别线程池单独开一个线程不能占用主线程相机帧到的回调也要避免在CameraCaptureSession的Callback里做任何耗时操作。性能指标上我会关注三个数字预览首帧延迟、识别帧率、平均识别耗时目标分别是小于200ms、稳定在30fps、单帧识别少于50ms。另外还要考虑低端机的兼容性比如关闭部分帧的识别来降低CPU占用或者在识别成功之后自动暂停预览避免重复触发。这种开放题没有标准答案但面试官能从你的方案里看出你有没有真正的Camera实战经验。如果你只是背过Camera2的基本用法写不出“YUV_420_888转NV21”这种细节也很难谈到ImageReader在BufferQueue上的内存管理。这个题的分数差距往往就是这里拉开的。4. 备考路线如果时间倒流我会这样准备商汤这类笔试商汤的笔试不是靠考前突击一周就能解决的它考察的内容覆盖面很广但又不像研究生考试那样有明确的考纲。你要做的是在有限时间内把“最重要、最高频、最能拉分”的内容先拿下而不是全网找资料最后什么都看了却什么都没记住。4.1 先搭好环境再谈刷题很多人在准备笔试阶段花大量时间刷面试题却忽略了动手环境。但商汤这套笔试里其实有一些题目如果你在Android Studio里实际跑过会理解得更透彻。比如自定义View的测量模式你光看文章很难形成直观感受但如果你在Android Studio里写一个demo分别设置match_parent、wrap_content、具体dp值再在onMeasure里打日志你立刻能理解MeasureSpec是怎么传进来的。我当时用的就是Android Studio官方最新稳定版配上自己电脑上已经下载好的SDK。这里要提醒一句不要为了追求新功能去装预览版或Beta版笔试前的环境一定要稳你也不想在调试一个demo时被编译器的偶发问题卡住半小时。AGP和Gradle版本不匹配是我见过最多的环境坑你如果用的是比较新的Android Studio版本一定要检查项目的Gradle版本是否兼容否则一同步就报错特别浪费时间。为了快速验证各种知识点我建议你本地准备一个“题库练习工程”里面新建一些单独的Activity或自定义View来复现题目场景。比如说你可以写一个TestEventActivity在布局里嵌套几个不同的事件拦截逻辑通过日志输出每次触发的dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent很快就能把事件分发的流程吃透。纸上学来终觉浅这句话在Android笔试上尤其适用。4.2 时间分配别把时间都花在啃源码上我见过不少备考的人从Java集合源码一路啃到OKHttp源码再啃到Glide源码觉得自己很扎实。但问题是商汤的笔试更偏向“原理 场景应用”的组合不会直接问你Glide的缓存策略有哪些层级。你要把时间优先投入到以下这几个板块四大组件生命周期与启动模式必须烂熟于心结合场景分析自定义View的measure/layout/draw流程高频难度大拉分明显事件分发机制结合滑动冲突多线程与线程池Callable、Future、ExecutorService的搭配使用JVM内存结构与GC机制结合内存泄漏、OOMHandler与Looper消息机制必须会画流程图Bitmap高效加载与缓存LruCache、inSampleSize、复用Android网络框架基础不会直接考OKHttp源码但会考图片加载和弱网优化思路我当时的备考节奏是前两周重点攻Java并发和Android View体系把这两块吃透因为它们是一旦理解就不会忘的底层知识后两周专项刷题针对Handler、事件分发、Bitmap、启动模式这几个高频考点做“概念复述 场景分析”的训练。每一道题都尝试用“背景、原理、应用、优化”四段式来整理答案这样到了笔试题量比较大的时候不容易卡壳。4.3 该重点关注哪些题型和资料资料上我不建议你盲目追求“面试题合集”这类资料因为整理质量参差不齐有些答案还是错的。我个人比较推荐的有两条主线一是《Android开发艺术探索》里关于View事件体系、RemoteView、WindowManager、Handler的章节。这本书虽然出版时间早于2018年但底层原理的东西至今没有过时尤其是事件分发那节把源码分析得非常透彻非常适合笔试复习。二是往年的笔试真题和面经。牛客网和CSDN上有不少商汤Android笔试的回忆帖虽然很少有人能把完整原题贴出来但至少能让你心里有数题量大概多少是单选多选还是编程题有没有简答题。我当时整理了网上能找到的所有回忆题打印出来对着题目自己复述答题要点比自己单纯看知识点效率高很多。另外如果你时间比较宽裕可以把Camera2的基础使用流程过一遍。哪怕你之前没有做过Camera开发也要理解“打开相机、创建CaptureSession、设置预览Surface、处理ImageReader回调”这四步。因为商汤的Android业务跟相机、图像强相关笔试里出现Camera相关开放题的概率非常高你没有经验也要有知识储备。5. 笔试之外的隐形考点工具链、代码习惯与心态很多人以为笔试就是做题只要知识点掌握得好就能拿高分。但实际上笔试过程中反映出来的代码习惯、答题心态、时间管理能力本身也是筛选的一部分。商汤这类技术公司尤其看重候选人“解决问题的方式”是否专业。5.1 代码书写与笔试平台的习惯商汤的笔试通常是在线OJ试卷要求你直接手写代码或伪代码。很多人在本地IDE里写得顺畅一上在线OJ就原形毕露要么类名写成了public class要么没有导入必要的包要么边界条件完全没考虑。我建议你在笔试前专门去牛客网的在线编程题库练几道题熟悉在线平台的输入输出格式和代码提交方式。尤其是商汤这类偏业务的公司笔试里的编程题往往不是纯算法题而是给你一个场景让你写出核心代码比如“实现一个LRU缓存”“实现一个带优先级和延迟的消息队列”。这种题目在本地IDE里可以随便起类名但在OJ平台上主类和入口方法的签名是必须严格匹配的一个字母都不能错。再者代码风格也很重要。如果题目要求手写代码尽量做到变量命名清晰、逻辑分层明确、关键步骤加注释。面试官在阅卷时看到的是一团乱麻还是清晰结构差别很大。我认识几个后来进了商汤的同学聊起来都说代码卷面分在总分里占的比重比想象中大。5.2 遇到不会的题怎么“抢分”笔试一定会碰到不会的题这是正常的没有人能拿满分。关键是遇到不会的题时你的应对方式能不能帮你多拿几分。对于选择题哪怕不会也要尽量用排除法缩小范围不要空着。商汤的笔试是机器阅卷人工复核结合选择题答错不扣分的话蒙一个总比空着强。对于简答题和开放题就算你没有完全把握也一定要把你想到的思路写出来。哪怕是写“这个问题我暂时没有直接解决方案但我会从A、B、C三个方向去排查”也比交白卷强。阅卷人想看到的不是一个完美答案而是你面对未知问题时的反应方式。很多人恰恰是栽在“不确定对不对所以干脆不写”上白白丢了过程分。我记得有一道关于性能和内存的开放题我一开始也只能想到布局层级优化和图片压缩这两个常规方向后来硬是把自己做过的Bitmap复用、inBitmap、硬件位图配置这些细节也写了进去。虽然不能确定这是不是最佳答案但至少让阅卷人看到我有实际经验而不是只会背优化清单。5.3 笔试之后的面试衔接商汤的笔试和面试距离很近有时候笔试结束后第三天就会收到面试通知。这时候你手里已经攒了一堆笔试中遇到的问题千万别考完就扔一定要趁热打铁复盘。我当时把笔试里所有不确定的题目整理成了一个文档每道题都去查资料、验证答案并把延伸的知识点一并补上。到了面试环节面试官问的问题跟我笔试复盘的内容重叠度非常高。比如笔试里考了事件分发面试就追问了一个“你如何解决一个复杂页面里的多点触控冲突”的项目题。如果你在笔试后没有做复盘现场很难在短时间内组织出有深度的回答。另外如果你在笔试的开放题里提出了某个技术方案最好在面试前把自己写的方案重新读一遍思考一下哪里可能存在漏洞。因为面试官很喜欢拿你笔试的答案做引子然后一步步往深里问看看你是真的理解还是为了应付题目临时堆砌的知识点。我那次就是因为笔试方案里写了“使用LruCache管理识别结果缓存”被面试官追着问了LruCache源码里LinkedHashMap的accessOrder到底是怎么回事。好在我复盘时刚好把这块啃了一遍不然很可能在这个环节露怯。最后分享一点我的体会商汤这场笔试与其说是在考知识不如说是在考“你像不像一个能直接下场干活的工程师”。很多题目的答案不是唯一的但逻辑清晰、考虑边界、结合实际经验的人拿到的分数一定比只会背书的人高。如果你现在还在备考阶段别焦虑把底层原理啃透把场景题练熟遇到不会的题就按经验推断并写清楚思路这套逻辑放到任何一场Android笔试里都不会错。