Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

系统设计场景题

中高级面试的开放设计题,考架构思维而非标准答案。重点是展示分析过程:先问清需求/约束 → 分模块 → 定接口 → 讲权衡 → 提优化。你的底层 + 性能 + 架构知识能在这里综合发挥。

一、答题通用框架(套路)

任何“设计一个 X“都按这个结构答:

  1. 澄清需求:功能边界、量级(QPS/数据量)、性能/内存约束、平台。先问再答,体现工程素养。
  2. 整体架构:分几层/几个模块,各自职责。
  3. 核心模块详细设计:数据结构、关键算法、接口设计。
  4. 关键问题处理:并发、缓存、生命周期、异常、内存。
  5. 权衡与优化:为什么这么选,有哪些 trade-off,怎么扩展。

记住:面试官要看思路和沟通,不是要你写完整代码。 边画图边讲。

二、设计图片加载库(最高频,如自研 Glide)

需求澄清:支持网络/本地、缓存、列表滑动场景、防 OOM、生命周期安全。

整体架构:

请求(with/load/into) → 生命周期绑定 → 缓存查找(内存→磁盘) 
→ 未命中则下载 → 解码(采样压缩) → 缓存写入 → 显示

核心设计:

  • 三级缓存:内存(LruCache,弱引用存活动资源)→ 磁盘(DiskLruCache)→ 网络。
  • 生命周期绑定:注入空 Fragment 监听宿主,onStop 暂停、onDestroy 取消请求(防泄漏)。
  • 防 OOM:按目标 View 尺寸 inSampleSize 采样压缩;Bitmap 复用池(BitmapPool)。
  • 线程模型:下载/解码在后台线程(线程池/协程),回调切主线程。
  • 请求管理:列表复用时取消旧请求(ImageView tag 绑定请求)。
  • 设计模式:建造者(请求构建)+ 单例(引擎)+ 观察者(回调)。

权衡:内存缓存大小(占内存 vs 命中率)、缓存淘汰策略(LRU)、采样精度 vs 质量。

三、设计网络请求框架(如自研 Retrofit + OkHttp)

  • 分层:接口定义层(注解/动态代理)→ 请求封装层 → 拦截器链 → 连接/IO 层。
  • 拦截器链(责任链):日志、重试、缓存、鉴权、加解密可插拔。
  • 连接池:复用 TCP 连接,减少握手。
  • 线程/异步:回调 / 协程 suspend / Flow。
  • 可扩展:Converter(序列化)、CallAdapter(返回类型)可替换。
  • 你的加分点:加解密拦截器、证书 Pinning、参数签名(结合你的安全背景)。

四、设计断点续传下载器

需求:大文件、断点续传、多线程加速、暂停/恢复、进度。

核心设计:

  • 分片下载:按文件大小切成 N 段,每段用 HTTP Range: bytes=start-end 请求,多线程并发。
  • 断点记录:持久化每段已下载偏移(数据库/文件),恢复时从断点续传。
  • 合并:各段写入同一文件的对应偏移(RandomAccessFile.seek)。
  • 状态管理:等待/下载中/暂停/完成/失败,状态机管理。
  • 完整性校验:下载后校验 MD5/SHA。
  • 优化:动态调整线程数、失败重试、弱网降级、用 WorkManager 保证后台续传。

五、设计 IM / 即时通讯

需求与约束:支持单聊/群聊、保证消息不丢不重不乱序、支持弱网、省电省流。

整体架构:

  • 客户端层:UI 层 → 本地存储层(SQLite) → 消息同步层 → 连接层。
  • 服务端层:接入层(负载均衡/长连网关) → 逻辑层(单聊/群聊) → 存储层(MySQL存会话, Redis存状态)。

数据流与状态机 (消息生命周期):

  1. 发送:生成本地唯一 ClientMsgID,状态设为发送中,存入本地数据库。
  2. 传输:通过长连接发送给服务端,服务端返回 ServerMsgID 确认接收。
  3. 确认:客户端收到 ACK 后,更新本地状态为发送成功;若超时未收到,状态转为发送失败(显示红点)。
  4. 接收:接收方收到 ServerMsgID,发送 ACK 确认,然后渲染到界面并存库。

核心问题处理:

  • 连接层选型:
    • WebSocket / TCP:主流采用自研 TCP 长连接或 WebSocket,心跳保活(按网络状态智能调节心跳间隔,减少电量消耗)。
  • 可靠性与去重 (ACK 与 Idempotency):
    • 必须有双向 ACK 机制。发送端根据 ClientMsgID 去重,接收端根据 ServerMsgID 去重。
  • 有序性:
    • 依赖服务端的递增序列号 (Seq) 保证消息严格有序。
  • 离线与同步机制:
    • 采用 推拉结合:服务端推送新消息通知,客户端拿着本地最大 Seq 拉取增量消息(Sync)。
  • 弱网与存储优化:
    • 采用本地 Room 数据库持久化聊天记录;断网时可看历史记录,发消息仅改本地状态,恢复网络后后台重发。

六、设计短视频 / Feed 流

需求与约束:无限滑动、秒开、播放不卡顿、降低 OOM 概率与发热。

客户端架构:

  • UI 控制层(RecyclerView) → 预加载策略层 → 播放器池(PlayerPool) → 本地缓存层。

核心数据流与 Paging3 集成:

  • 使用 Paging3 实现分页流:数据层通过 PagingSource 从网络或本地数据库拉取,ViewModel 将流转为 PagingData 喂给 UI,UI 层滑动触发下一页。

核心问题处理:

  • 预加载策略 (Preload):
    • 提前请求下 N 条(如 3 条)视频的元数据与首帧图片。
    • 视频缓冲:提前下载当前视频后的 1-2 个视频的前 1MB 块(Moov Box 优先)。
  • 播放器复用池 (Player Reuse):
    • 绝不能给每个 Item 创建 MediaPlayer/ExoPlayer。全局维护 1-3 个播放器实例池,滑动切换时动态将 DataSource 挂载到目标 Surface 上,降低内存分配与重建开销。
  • 缓存策略:
    • 边下边播(通过本地代理如 AndroidVideoCache):未缓存部分请求网络并写入文件,已缓存部分直接读本地。
  • 性能与内存隔离:
    • 滑出屏幕立即停止解码、释放播放器资源到池中。
    • 控制预加载深度:防止疯狂往下滑动导致内存暴增与大量废弃请求。
  • 失败与降级:
    • 网络波动时降级播放低码率;拉取失败时展示已缓存内容并提示网络异常。

七、其他常见设计题

  • 设计本地缓存框架:LRU + 磁盘 + 过期策略 + 序列化。
  • 设计埋点/APM SDK:数据采集 → 本地队列 → 批量上报 → 失败重试。和你 SDK 背景契合,可深入讲采集、性能影响最小化、上报策略。
  • 设计组件化路由:APT 生成路由表 + 按路径分发 + 拦截器。

答题要点总结

  1. 先问后答:澄清需求和约束是第一步,直接写代码是大忌。
  2. 分层分模块:展示你能把复杂系统拆解。
  3. 讲权衡:每个选择说清 trade-off(内存 vs 速度、一致性 vs 可用性)。
  4. 结合你的优势:涉及性能、内存、安全、SDK 的设计题,主动用你的底层经验加分——比如设计图片库讲 Bitmap 内存复用、设计网络框架讲 Pinning 和加解密、设计埋点 SDK 讲对宿主性能影响最小化。这些是普通应用开发者讲不出的深度。
  5. 画图沟通:边画架构图边讲,面试官看的是思路和表达。

进阶补充:标准追问清单、状态机与可靠性设计

每道系统设计题的追问清单

回答完主方案后,主动补 5 类追问:

  1. 容量与性能:QPS、并发、缓存、线程池。
  2. 失败与重试:超时、重试、幂等、降级。
  3. 状态机:任务有哪些状态,如何迁移,异常如何恢复。
  4. 一致性:本地/服务端状态如何同步,冲突如何处理。
  5. 可观测性:日志、指标、trace、告警。

下载器状态机示例

Idle -> WaitingNetwork -> Downloading -> Paused -> Downloading -> Completed
                         -> Failed -> Retrying -> Downloading

每个状态迁移都要说明触发条件、持久化字段和失败恢复。

限流、熔断、降级、幂等

机制解决什么
限流防止瞬时流量打爆系统
熔断下游持续失败时快速失败
降级非核心能力失败时保核心路径
幂等重试不会产生重复副作用

Android 特有追问

  • 进程死亡后如何恢复任务?
  • 前后台切换如何处理?
  • 弱网/断网如何重试?
  • 页面旋转或配置变更如何保存状态?
  • 本地缓存和服务端数据冲突时谁优先?

面试表达模板

“我会先定目标和约束,再拆模块,然后补状态机和失败处理。比如这个下载器,核心不是只把文件下下来,还要保证断点续传、进程死亡恢复、重复点击幂等、弱网重试和可观测性。”

**追问:**为什么系统设计不能只画模块图?因为真实系统最容易出问题的是状态迁移、失败恢复和边界条件,这些必须在设计里说明。