客户端架构:为什么、什么时候、怎么做

客户端架构:为什么、什么时候、怎么做https://blog.csdn.net/mix39/article/details/161257993客户端设计(上):MVC/MVP/MVVM 与高内聚低耦合https://blog.csdn.net/mix39/article/details/161257807客户端设计(中):OOP、SOLID 与设计模式https://blog.csdn.net/mix39/article/details/148036409客户端设计(下):场景流派与实战设计方式https://blog.csdn.net/mix39/article/details/161322269?spm=1001.2014.3001.5502

客户端架构:为什么、什么时候、怎么做

一、为什么需要架构

代码量涨上去之后,没有架构的系统会:

  • 改一处崩三处——耦合太重,牵一发动全身
  • 新来的人看不懂——没有边界,没有规则,每个类都能碰任何东西
  • 没人敢重构——不知道动了会影响谁
  • 越迭代越慢——加功能的成本指数级上升

架构本质就是在回答一个问题:怎么让系统在变大的过程中,改东西的成本不跟着变大?

具体到客户端:

问题

架构解法

团队互相踩

模块拆边界,各管各的

改不动

接口隔离,内部变了不影响外部

改崩了

分层约束,UI 不能直接调底层实现

各写各的

统一机制,消息/生命周期/跳转各一套规则

一句话:架构就是让你改代码的时候心里有底。


二、什么时候需要架构

需要

  • 多人协作——规则不立,就是互相踩
  • 长期迭代——活三年的项目,不架就越来越慢
  • 复杂的业务域——逻辑缠绕的必须理清
  • 有性能要求——冷启 2 秒内、帧率 60fps,没约束做不到了才补就晚了
  • 跨团队依赖——模块被十几个团队调,接口没定义好就是灾难

不需要

  • 一次性工具/脚本——用完就扔
  • 原型验证/PoC——验证想法的阶段,架构是浪费
  • 需求还没定形——方向都没定,先架构就是先入为主
  • 一个人写、500 行代码——过度设计比没设计还坑

判断标准

改代码的成本会不会失控?

  • 不会失控 → 不需要架构,别提前设计
  • 已经觉得改东西心虚了 → 该架构了
  • 还没写但预判会复杂 → 提前定边界,不用定死细节

架构是解决问题的,不是证明自己专业的。 200 行代码搞六层架构,不叫架构,叫过度设计。


三、架构怎么分

3.1 三种分法

按类的类型分(MVC/MVP/MVVM)

ui/   → Activity、Fragment
vm/   → ViewModel
repo/ → 数据层
model/→ 数据模型
  • 解决:同一功能的 UI 和逻辑解耦,方便单元测试
  • 适用:单人或小团队,模块内部组织
  • 坑:业务一多,所有业务的 vm 全挤在一个包里

按业务分

chat/     → 聊天
contacts/ → 通讯录
discover/ → 发现
me/       → 我的
  • 解决:团队边界清晰,互不干扰
  • 适用:多业务线并行迭代
  • 坑:公共能力放哪?每个业务自己写一套就重复了

按组件分

network/    → 网络层
image/      → 图片加载
badge/      → 角标组件
navigation/ → 路由
storage/    → 存储
  • 解决:公共能力复用
  • 适用:跨业务的基础设施
  • 坑:业务逻辑不能放这里,否则组件变成大杂烩

3.2 真实项目:三层混合

┌─────────────────────────────────┐
│  业务层(按业务分)               │  ← 团队边界
│  chat / contacts / discover / me │
├─────────────────────────────────┤
│  组件层(按组件分)               │  ← 复用边界
│  badge / network / image / router│
├─────────────────────────────────┤
│  模块内部(按类型分)             │  ← 代码组织
│  ui / vm / repo / model          │
└─────────────────────────────────┘

规则

  1. 对外看业务/组件——负责哪个模块就只动哪个
  2. 跨模块调接口不调实现——用 api 模块,不直接依赖实现
  3. 模块内部才按类型分

宏观按业务/组件划分团队边界,微观按类型组织模块内部代码。 分法只是手段,关键是让每个人知道自己的边界在哪。


四、架构与设计

架构

设计

回答什么

规则和边界

怎么实现

粒度

模块级

类/方法级

变的原因

业务方向变了

需求变了

变的影响

牵一发动全身

改了就改了

关注点

不能做什么

怎么做

架构定规则,设计定实现。架构是骨架,设计是肌肉。骨架再好,肌肉长歪了也跑不动。


五、架构规则(按常用程度排序)

5.1 模块与依赖

依赖规则

依赖方向只能从上往下,不能反过来

业务层 → 组件层 → 基础层
   ✅                ❌ 基础层不能调业务层
  • 不能循环依赖——A 依赖 B,B 又依赖 A,就是没架构
  • 接口所有权看场景——内部模块间依赖倒置时,接口归调用方;对外 SDK 接口,定义权归提供方
  • 用 api/implementation 控制依赖传递——implementation 改了不触发下游编译
通信规则

模块之间怎么说话,架构必须定死:

方式

适用场景

直接接口调用

同进程、强依赖

耦合

回调/Listener

一对一、异步结果

回调地狱

EventBus

一对多、松耦合

链路不可追踪

LiveData/Flow

UI 响应数据变化

生命周期要管好

路由

跨模块跳转

参数类型不安全

规则:对外通信方式要统一入口,不要散落各处

路由与导航
  • 统一路由表——所有页面注册在路由表,不允许硬编码类名跳转
  • 路由拦截器——登录拦截、降级拦截、埋点拦截,统一在链路上插
  • 路由降级——目标页面不存在(版本低/模块未加载),跳兜底页不是崩溃
  • 路由参数校验——必填参数缺失要在路由层拦住,不能到了目标页才崩
  • Deep Link——外部跳转必须过路由,不能绕过拦截器
演进规则
  • 防腐层——老接口不能直接暴露给新模块,中间包一层适配
  • 绞杀者模式——新功能走新架构,老功能逐步迁移,别一次性重写
  • API 版本化——对外接口加版本号,老版本至少兼容两个大版本

5.2 状态与数据

状态管理

Single Source of Truth——一个状态只有一个地方是权威的

  • 两个地方都写同一个状态 → 必出 bug
  • 例:聊天 Tab 的未读数,权威数据源在 MessageManager,别的都是读
  • 例:Tab 的选中状态在 TabBarManager,不能谁都能改
缓存策略

缓存不是想加就加的,架构要定规则

  • 层级——内存 → 磁盘 → 网络,每层失效策略不同
  • 一致性——磁盘缓存和内存状态不一致怎么办?以谁为准?
  • 淘汰——LRU / TTL / 容量上限,不能无限涨
  • 穿透——读不到要不要兜底值?兜底值要不要缓存?
离线与同步

没网不是空白页

  • 离线可用——核心功能必须有离线数据兜底
  • 数据同步策略——增量还是全量?推还是拉?冲突怎么解?
  • 写后同步——离线操作先存队列,有网后自动同步,不是丢掉
  • 同步状态可见——用户知道当前数据是不是最新的
状态保存与恢复

被系统杀了杀回来不能是空白

  • onSaveInstanceState——关键状态必须保存,不能丢了
  • 恢复顺序——先恢复框架状态,再恢复业务状态,再恢复 UI 状态
  • 任务栈恢复——深任务栈被杀后回来,要能恢复到之前的页面
  • 防抖防重——恢复过程中触发的操作要做防抖,不能重复执行

5.3 稳定性与容错

异常处理

错误类型

策略

可恢复错误

重试 + 降级,用户无感

不可恢复错误

上报 + 降级到安全状态,不崩溃

第三方 SDK 错误

隔离 + 兜底,不拖垮宿主

数据格式错误

兜底值续命,不让异常传播

红线:业务模块的异常不应穿透到上层

降级与熔断

防止一人炸全锅

  • 熔断——某个模块崩了不能拖垮整体
  • 超时——跨模块调用必须设超时,不能无限等
  • 降级——图片加载失败 → 占位图 → 灰色占位 → 不展示,每层都有兜底

防止自己炸自己

  • 防重入——同一个方法不能并发进入
  • 防泄漏——注册的监听必须能反注册
  • 防雪崩——批量操作不能同步全量,要分批+异步
容灾
  • 模块健康检测——连续崩溃 N 次自动禁用
  • 兜底页——加载失败不是白屏
  • 热修复通道——线上 bug 快速修复,缩短修复周期(有范围限制:不能改资源和类结构)
  • 数据修复——关键数据启动时校验 + 兜底值修复
资源管理

资源不是用完就算的,架构要管生命周期

  • 注册必反注册——监听器、广播、Observer,泄漏就是 bug 源头
  • 大对象主动回收——Lottie 动画资源什么时候释放?不能等 GC
  • 连接/流的生命周期——HTTP 连接、长链接,谁负责关?

5.4 性能与资源

性能预算

架构要分配资源配额,不然就是丛林法则

  • 启动时间预算——整体冷启上限多少?每个模块分多少?
  • 内存预算——图片缓存上限多少?Lottie 同时播放几个?
  • 帧率预算——Tab 切换动画不能卡,哪个环节是瓶颈要提前定
  • 线程池配额——核心线程数、队列大小,不能一个模块占满
线程模型

谁跑哪个线程,架构必须定义

  • 每个模块的线程归属——UI 操作必须在主线程,IO 操作必须有独立线程池
  • 线程池是共享还是独占——共享省资源但会互相影响
  • Handler 用谁的——不能用 new Handler(),必须明确是主线程 Handler 还是模块私有 Handler
  • 并发边界——哪些操作是原子的,哪些需要加锁,加什么锁
包体积治理

架构阶段不考虑体积,后面减不动

  • 模块体积预算——每个模块多大,增量不能超阈值
  • 资源管控——图片/字体/Lottie 资源统一管理,不能重复引入
  • 代码裁剪——ProGuard/R8 规则统一,反射要有 keep 规则
  • 动态下发——非核心资源走动态加载,不打包进 APK
动画架构

不是想怎么动就怎么动

  • 动画统一管理——所有动画走统一调度,不能多个动画同时抢主线程
  • 动画降级——低端机禁用复杂动画,架构层控制
  • 动画中断——页面退出时正在播的动画必须能立即停止
  • 动画资源管控——Lottie/属性动画资源统一管理,不能无限加载

5.5 初始化与启动

启动顺序是架构问题,不是编码问题

  • 依赖拓扑必须有向无环——A 初始化依赖 B,B 依赖 C,不能 C 又依赖 A
  • 懒加载 vs 预加载必须明确策略——哪些启动时必须 ready,哪些等到用的时候再初始化
  • 初始化失败要有兜底——不能因为一个非核心 SDK 初始化失败,整个 App 起不来
  • 初始化时序可观测——每个阶段耗时打点,启动慢了能定位

5.6 配置与灰度

配置中心化

所有开关必须有一个统一入口

  • 开关不能散落各处——今天一个 isXxxEnabled,明天一个 XxxSwitch,找都找不到
  • 配置必须有默认值——服务端没下发、网络超时,App 不能挂
  • 配置变更要能推送——开关改了,正在跑的逻辑怎么响应?轮询还是推送?
  • 配置要有粒度——全量开关、灰度开关、AB 分流,不同粒度不同机制
灰度与实验体系

不是上线就完事,架构要支持灰度

  • 实验维度——按用户/地区/版本/设备分级
  • 实验互斥——两个实验不能同时影响同一段逻辑
  • 实验回滚——出问题要秒级关掉,不是等发版
  • 实验结果可归因——指标变化能对应到具体实验

5.7 可观察性

埋点架构

不是打一个 Log 就叫埋点

  • 事件模型统一——所有埋点遵循同一 schema:who/when/where/what/how
  • 埋点和业务解耦——不能在业务逻辑里到处插埋点代码,走 AOP 或拦截器
  • 埋点可靠性——关键埋点必须保证上报成功,丢了要有补发机制
  • 埋点去重——快速切 Tab 不能同一事件上报 10 次
日志体系

不是 Log.d 就完了

  • 结构化日志——每条日志带模块名/级别/上下文,不是一坨字符串
  • 日志分级——开发日志/关键路径日志/线上诊断日志,级别不同策略不同
  • 线上日志隐私——用户数据脱敏,不能明文打出来
  • 日志采样——不是每条都上报,高频日志要采样,否则流量炸
APM 与监控

性能不是出问题了才查

  • 启动监控——冷启/温启/热启各阶段耗时,自动上报
  • 帧率监控——掉帧率、卡顿堆栈,线上可追溯
  • 内存监控——内存水位、GC 频率、大对象分配
  • 网络监控——请求成功率、耗时分布、错误码分布
  • 卡顿检测——主线程 > 300ms 的操作自动抓堆栈
  • ANR 监控——主线程耗时拦截 + 卡顿堆栈采集,接近 ANR 阈值时告警

5.8 安全与隐私

安全边界
  • 模块间数据不能互读——你的 SP 我不能直接访问,要走接口
  • 敏感操作必须鉴权——功能能不能展示、操作能不能执行,不只靠 UI 判断
  • 序列化边界——跨进程/跨模块传递的数据要校验,不能信任对方
隐私合规

不是法务的事,架构必须支持

  • 数据采集审批——任何用户数据采集必须过审批流程,架构上要卡住
  • 敏感数据脱敏——日志、埋点、上报中的手机号/身份证必须脱敏
  • 权限使用审计——每次调摄像头/定位/通讯录要有审计记录
  • 数据生命周期——用户数据什么时候创建、什么时候删除,架构要管
  • 合规开关——不同地区不同法规(GDPR/个保法),架构要支持按区配置
权限模型

谁能动什么,架构要管

  • 功能权限——这个 Tab 能不能展示、这个按钮能不能点
  • 数据权限——模块 A 能不能读模块 B 的数据
  • 运行时权限——Android 权限申请流程统一封装,不能各写各的

5.9 设计系统

UI 一致性不是靠设计师盯,是靠架构兜底

  • Design Token——颜色/字号/间距/圆角全部 token 化,不能硬编码
  • 组件标准化——按钮/卡片/列表项统一组件,不允许各写各的
  • 主题切换——暗色/亮色/品牌换肤,架构要支持一层切换全局生效

5.10 工程化

编译架构

模块怎么拆影响编译速度,编译速度影响开发体验

  • 模块粒度——太粗改一行全量编译,太细依赖关系爆炸,要平衡
  • api vs implementation——依赖传递必须控制,一个模块 api 暴露多了,改内部实现全量编译
  • 编译缓存——哪些模块稳定不改,可以预编译成 aar
  • 构建变体——debug/release/minimap 要隔离,不能 debug 代码泄露到线上
SDK 接入规范

第三方 SDK 是最容易出事的

  • 隔离层——所有第三方 SDK 必须封装一层,业务不能直接调 SDK API
  • 初始化管控——SDK 初始化统一在初始化中心管理,不能散落在各处
  • 版本锁定——SDK 升级必须全量回归,不能悄悄升
  • 崩溃隔离——SDK 崩溃不能拖垮宿主,try-catch 包在最外层
  • 权限最小化——SDK 申请的权限必须审核,不能要什么给什么
模块版本与发版

模块独立不是口号,要靠版本管理落地

  • 语义化版本——大版本/小版本/补丁,什么时候升什么
  • 兼容承诺—— api 模块的接口改了,多少个版本内必须兼容老接口
  • 发版节奏——模块独立发版还是跟宿主走,架构决定
  • 依赖声明——模块 A 声明依赖模块 B 的 [2.0, 3.0),不能隐式依赖
插件化与动态化
  • 模块能不能热加载——哪些必须内置,哪些可以动态下发
  • 版本兼容——宿主升级后,插件老版本能不能跑?兼容几代?
  • 降级兜底——动态模块加载失败,用内置兜底还是白屏?

5.11 多进程架构

不是所有东西都能跑同一个进程

  • 进程划分——主进程、push 进程、webview 进程、工具进程,为什么这么分
  • IPC 边界——跨进程怎么通信?ContentProvider / AIDL / Messenger,选什么
  • 进程间状态同步——主进程状态改了,其他进程怎么知道?
  • 进程复活——拉起来之后状态恢复谁管?

5.12 测试策略

  • 模块可测试性——依赖注入,不能 new 死实现
  • 接口可 mock——跨模块依赖必须有接口层
  • 关键路径有集成测试——启动链路、状态切换、关键业务流

六、架构规则速查表

排序

大类

小类

核心规则

1

模块与依赖

依赖规则

单向不循环,接口所有权看场景

2

模块与依赖

通信规则

统一入口,不散落

3

模块与依赖

路由与导航

统一路由表 + 拦截器 + 降级

4

模块与依赖

演进规则

防腐层 + 绞杀者 + API 版本化

5

状态与数据

状态管理

Single Source of Truth

6

状态与数据

缓存策略

层级 + 一致性 + 淘汰 + 穿透

7

状态与数据

离线与同步

离线可用 + 同步策略 + 写后同步

8

状态与数据

状态恢复

保存/恢复顺序 + 任务栈 + 防抖

9

稳定性与容错

异常处理

不穿透 + 分级处理 + 隔离第三方

10

稳定性与容错

降级与熔断

降级/熔断/超时/防重入

11

稳定性与容错

容灾

健康检测 + 兜底页 + 热修复 + 数据修复

12

稳定性与容错

资源管理

注册必反注册 + 大对象主动回收

13

性能与资源

性能预算

时间/内存/帧率/线程池配额

14

性能与资源

线程模型

归属明确 + 池化 + 并发边界

15

性能与资源

包体积

模块预算 + 资源管控 + 代码裁剪 + 动态下发

16

性能与资源

动画架构

统一调度 + 降级 + 中断 + 资源管控

17

初始化与启动

拓扑无环 + 懒预加载策略 + 失败兜底

18

配置与灰度

配置中心化

集中管理 + 默认值 + 推送

19

配置与灰度

灰度与实验

分级 + 互斥 + 秒级回滚

20

可观察性

埋点架构

事件模型 + 解耦 + 可靠 + 去重

21

可观察性

日志体系

结构化 + 分级 + 脱敏 + 采样

22

可观察性

APM 与监控

启动 + 帧率 + 内存 + 网络 + 卡顿 + ANR

23

安全与隐私

安全边界

数据隔离 + 鉴权 + 序列化校验

24

安全与隐私

隐私合规

采集审批 + 脱敏 + 审计 + 生命周期 + 合规开关

25

安全与隐私

权限模型

功能 + 数据 + 运行时三级管控

26

设计系统

Token 化 + 组件标准 + 主题切换

27

工程化

编译架构

模块粒度 + 依赖控制

28

工程化

SDK 接入

隔离层 + 初始化管控 + 崩溃隔离 + 权限最小化

29

工程化

模块版本与发版

语义版本 + 兼容承诺

30

工程化

插件化与动态化

热加载 + 版本兼容 + 降级

31

多进程架构

进程划分 + IPC + 状态同步 + 进程复活

32

测试策略

DI + 接口可 mock + 关键路径集成测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值