返回 更多
更多
17 分钟阅读

关于我在技术的这一面

面试关键词接龙,以及「答不上来是状态问题还是知识盲区」这件事

我是一名 Android开发工程师,这个是从传统编程时代一路走过来的职业岗位,对于Java、Kotlin以及Android SDK,是靠以前的学习模式去理解的。

Android的一些核心内容

我还是相信我自己对于Android开发业务的理解,Android开发,几大核心内容如下:

  1. 高质量还原设计部门的UI稿件。
  2. 确保整一个产品流程不出现异常情况。
  3. 确保Android的整体架构以清晰可控的数据链路去控制,这个时候就是介入MVP、MVVM、MVI等等架构模式。
  4. 确保Android APP在性能上的一个高效运行,这一个就很考验人了。
    1. 里面涉及到Android主线程的代码优化,不能在MainThread做一些耗时操作阻塞了UI渲染造成卡顿。
    2. 布局不能嵌套太深,例如LinearLayout嵌套了一大堆的东西,这样极其容易造成反复测量等等问题。
    3. RecyclerView里面滑动时偶发卡顿,帧率从 60 掉到 30,你会怎么排查?朝着 Bitmap 解码、还是GC抖动这些方向去寻找答案?
    4. 整体的APP太消耗流量、手机电量等等内容了,要如何去优化呢?
    5. 其他部门的同事质疑你的修复到底是不是真的有所优化?能否提供一份合理的数据报告给到他们,让他们看到自己行之有效的修复呢?
  5. 效能提升
    1. 例如在实现多语言翻译的时候,一般产品会提供一个Sheet,把所有字段的翻译内容整理在这里。你要怎么样去处理这些问题呢?使用Python脚本还是Gradle脚本去把Sheet里面的内容映射到strings.xml还是怎么样呢?
    2. 同事向你吐槽,这个项目太大了,Gradle编译一次都要十分钟,太影响开发效率了?这个时候要怎么样去优化Gradle的内容呢?
  6. 稳定性与线上质量,这一个往往比「功能能不能做」更决定用户留不留。
    1. 用户反馈 APP 闪退了,你拿到的是一段混淆后的堆栈,你怎么从这一坨信息里还原出真正出错的代码行?
    2. ANR 弹窗出现了,你会先看主线程在等什么——是 IO、锁、还是 Binder 调用卡住了?trace 文件要怎么读?
    3. 线上有一个 Crash 率突然从 0.1% 飙到 2%,你是先回滚版本,还是先热修复?依据是什么?
    4. 灰度发布只放了 5% 的用户,指标就已经不对劲了,你会怎么跟产品、测试、运维一起决定「停还是继续」?
  7. 版本兼容与设备碎片化,Android 和 iOS 最大的差异之一就在这里。
    1. minSdk 还卡在 21 的时候,新 API 想用了怎么办?你是写 if-else 分支,还是上 AndroidX 的兼容库?
    2. 某个功能在 Pixel 上正常,在华为、小米、OPPO 上表现不一致——是厂商 ROM 改了系统行为,还是你自己没测到?怎么建立一套可复现的测试矩阵?
    3. 从 Android 10 的分区存储到 Android 13 的通知权限,每一次大版本适配你有一套 checklist 吗,还是每次发版前临时抱佛脚?
    4. 折叠屏、平板、横竖屏切换,你的布局是写多套 layout,还是交给 Compose 的自适应方案?
  8. 网络与数据层,用户大部分时间在弱网或地铁里。
    1. 接口超时了,你是无限重试、指数退避,还是直接给用户一个「网络不佳」的友好提示?
    2. 列表数据要不要做本地缓存?缓存策略是 Stale-While-Revalidate,还是过期就全量刷新?Room 和 DataStore 你会怎么选?
    3. 后端接口字段改了名、删了字段,老版本 APP 还在跑,你怎么保证不 Crash?Protobuf、JSON 容错、版本协商,你倾向哪一种?
    4. 图片、视频这类大资源,CDN、预加载、WebP/AVIF 格式转换,你会从哪个环节下手省流量?
  9. 组件化与工程化,项目从十个人涨到五十个人的时候,这个问题躲不掉。
    1. 业务模块之间怎么解耦?ARouter 还是 Navigation Component?公共 Base 模块会不会变成「上帝模块」?
    2. 两个小组同时改同一个 Module,Merge Conflict 天天爆,你是拆仓库、拆 Module,还是上 Monorepo 的依赖管理?
    3. 基础库(网络、图片、埋点)要升级大版本,业务方不愿意配合改,你怎么推动全量迁移?
    4. Feature Flag 或者动态配置,能不能让某个功能只对特定用户、特定渠道打开,而不必发一个新包?
  10. 安全与合规,这一块产品往往不重视,出了事全是你的锅。
    1. 用户 Token、密钥写死在代码里,或者被反编译扒出来了,你怎么补救?ProGuard/R8 混淆够吗,要不要上加固?
    2. HTTPS 证书校验被 hook 绕过了,中间人攻击怎么防?Certificate Pinning 值得做吗,代价是什么?
    3. 隐私政策更新了,工信部、应用商店、Google Play 各有一套要求,你怎么确保 SDK 清单、权限声明、数据收集说明都对得上?
    4. 剪贴板读手机号、后台定位、自启动,这些敏感行为你怎么在代码层面做开关和审计?
  11. 测试与质量门禁,「能跑」和「能上线」之间差着一截。
    1. 核心业务逻辑你写单元测试吗?ViewModel、Repository 这种层测不测?覆盖率多少算够,还是看关键路径就行?
    2. Espresso 或 Compose UI Test 跑一遍要多久?CI 里每次 PR 都跑全量,还是只跑冒烟用例?
    3. 发版前回归测试总漏 bug,你是加强 Checklist、引入自动化,还是接受「线上再修」的节奏?
    4. Mock 后端接口的时候,MockWebServer 够用吗,还是需要一套独立的测试环境?
  12. 技术演进与选型,高级工程师不是只会写代码,还要会「什么时候不该写」。
    1. 新项目 UI 层用 Jetpack Compose 还是继续 XML?团队 Kotlin 熟练度、设计稿复杂度、招聘难度,你怎么权衡?
    2. 协程、Flow、RxJava 三套异步方案项目里并存,你是统一收敛,还是「新代码用新的、老代码不动」?
    3. 引入一个三方 SDK(推送、统计、支付),除了看文档,你还会评估包体积增量、隐私合规、和现有栈的冲突吗?
    4. 遗留代码又臭又长没人敢动,你是大爆炸式重写,还是绞杀者模式一点点替换?怎么跟老板解释「这段时间看起来没产出」?
  13. 跨端协作与交付,Android 工程师不是孤岛。
    1. 和后端定接口,REST 还是 GraphQL?分页、错误码、幂等性这些约定谁来写文档、谁来 enforce?
    2. iOS 同事说「我们那边已经上线了」,Android 还卡在厂商渠道审核,你怎么对齐发版节奏和用户预期?
    3. Deep Link、App Link、Universal Link 三套跳转,产品给的 H5 链接能不能正确唤起 APP 并落到指定页面?
    4. 设计稿是 Figma,标注是 Zeplin 还是蓝湖,开发和设计对「像素级还原」的标准一致吗?差几个 dp 算不算 bug?
  14. 可观测性与数据驱动,「我觉得优化了」和「数据证明优化了」是两回事。
    1. 埋点方案谁定?是自研还是接 Firebase、神策、GrowingIO?事件命名规范怎么避免「同一个按钮三个名字」?
    2. 关键漏斗(注册、支付、分享)转化率掉了,你怎么区分是代码 bug、UI 改版,还是外部流量变化?
    3. 日志分级、本地 ring buffer、线上采样上报,怎么在「够排查」和「不泄密、不撑爆存储」之间找平衡?
    4. 性能指标(启动耗时、帧率、内存)是手动 Benchmark 测一次,还是接入 APM 持续监控?Baseline Profile 你用过吗?

但是这些事情要怎么去聊呢?而且上面的这些问题随便找一个出来,其实一整个团队去商讨一个下午,都不一定有最终答案。

为什么很多人,总是寄希望在一次短暂的面试中,就能从求职者的角度听到自己想要的答案呢?

Android启动模式的套路

比如你问我,Android的启动模式下面,SingleInstance是怎么样去运转的,甚至你举一个例子,例如常见的:

ActivityA和ActivityC是standard模式的,ActivityB是singleInstance模式的。
如果现在是以 A -> B -> C -> B 为启动顺序,然后逐个再返回,Activity的返回结果是怎么样的?

我脑子会在那一刻宕机,不知道如何作答。

从HashMap到Concurrent的套路

Java的HashMap在1.7和1.8之中,他的数据结构有哪些变化?

然后再追问:

红黑树这个内容,可以详细讲讲他的实现结构吗?然后衍生出来问AVL树,再到深度优先遍历什么的。

通过这一个链路就可以大致得知,这个人对于一些所谓的底层原理理解得到不到位。

或者换一个实际问题去构建链路:

Java里面一个ArrayList可不可以forEach的过程中,不断remove一些元素?

  • 如果说可以,直接用一个不那么冒犯的方式结束这一轮的对话。可以得到的建模是:这一个人确实是不知道这些内容的。
  • 如果回答的是不可以,那就追问为什么不可以呢?
    • 然后套出ConcurrentModificationException这一个关键词,接着就可以用Java并发编程里面的内容进一步问了。
      • 在第一感觉不那么差的情况下,可以对人友善一点的话,就简单问一下,怎么去处理这一块的问题,可能就是简单问到同步锁机制就行了,或者是synchronized或者是Lock,知道一下乐观锁和悲观锁的概念就行了。
      • 如果问这些内容实际的目的,比如面试过程中是公司想要捡漏一样(用一个一般的薪资招聘到一个高级工程师甚至架构师),或者是作为高级工程师想要欺负一下年轻的工程师彰显一下自己的地位,那也可以深入去聊锁机制的各种问题。
      • 甚至你还可以去问他:让你来设计Java的并发框架,能不能比Doug Lea更技高一筹?

这是很好的一个聊天链路,作为上位者可以快速在一场对话的场景之中,顺利展开聊天进展。

作为上位者,你不需要自己真的太懂这一切的内容,你可以直接看着你准备好的题型去询问,提前背好你想要问的问题,反正主动权在你手上。

类似于Java的并发模型,你就可以按照这一条关键词去实现一个接龙过程:

HashMap红黑树ConcurrentModificationExceptionsynchronizedAQSCAS → ...

这对于你要去聊的这个Android开发工程师作出一个简要的人物建模,可以快速判断一下他的技术能力到底符不符合自己的预期。

哲学流角度的审视

可是,人真的有这么厉害吗?你说到任何一个关键词的时候,他都能立刻接上来?

你会在内心觉得这个事情有点假吗?

于是我在想的一个问题,也是自我宽慰自己的一个念头。

当我答不上来对方的这一大堆的信息密度的提问的时候,那是我的状态不好,也是那一个瞬间的我不会,并不是我这个人没有这个能力去掌握那一个点。

于是宿命向我发出信号,无法第一时间接上对方的话题,彼此之间缘分尚浅。一次对话就可以了,不需要再有后面的关系建立。

再厉害的人,都可以找到一万种方式去把他聊垮下去。

当然我也清楚地意识到:这个东西作为技术面试官,面对面试者的时候,那又到底什么样的问题才是合适的问题呢?

偶尔答不上来是状态问题。连续答不上来就可能是知识盲区。这是两回事。

这到底要怎么去界限呢?

比较好的模式是:对话的模式应该是,上位者这一方需要以一种引导的方式,去让下位者以一种良好的方式表述出你想要的内容。

大部分人,都不会有这样的包容心的吧。直接面试下一个人,这样多快。

不过这一切,都是缘分啦。