
大家都是 Android 开发老手(现在还哪有新手?),应该知道架构设计能力是区分资深与普通开发者的核心标志。
下面我问 15 个问题,这些问题直击 Android 架构的关键痛点与实践精髓,它们既是面试高频考点,更是日常开发中避坑指南。
1. 为什么 MVVM 在项目后期会变得臃肿
MVVM 之所以长盛不衰,核心是与 Android 的生命周期感知组件天然契合------ViewModel 能无缝处理配置变更、暴露可观察状态,还能清晰分离UI渲染与业务逻辑。理论上,它完美平衡了简洁性与结构性,堪称理想架构模式。
但现实往往骨感。
很多团队初期思路清晰,后期却把 ViewModel 当成了"万能容器":网络请求悄悄塞进来,数据库操作偷偷加进去,数据映射逻辑越堆越多,最后连导航决策也往里塞。原本的状态持有者,不知不觉变成了包揽一切的服务层,MVVM 架构就此崩塌。
可能很多小伙伴一直没有明白"负责协调 UI 状态"到底什么意思?简单的来讲,就是 UE 上的逻辑。即从仓库获取数据后,如何将这些数据显示在UI上,这个逻辑以及UI的状态,正是 ViewModel 职责所在!
各位老手应该都懂:MVVM 的核心是"职责边界"。ViewModel 只该负责协调 UI 状态,业务规则交给用例或领域服务,数据获取交给仓库(Repository),数据映射交给专门的转换器。把 ViewModel 精简到只做协调工作,MVVM 才能真正可持续。
关键认知是:MVVM 不是模式本身有问题,而是团队把它当成了"顺手放东西"的便利贴,才导致架构失控。
2. Clean Architecture 在 Android 项目中到底是什么

很多开发者觉得,建个 data、domain、presentation 文件夹,就叫 Clean Architecture 了。但这种只重表面结构的做法,根本没 get 到其精髓。
Clean Architecture 的核心是"依赖方向":内层必须独立于外层。领域逻辑不该知道数据来自 Retrofit 、Room 还是模拟实现,更不该依赖 Android 框架类------领域层要以最纯粹的形式承载业务规则。
在 Android 项目中,这意味着:在领域层定义仓库接口,在数据层实现接口;UI 层只和领域用例交互,绝不直接依赖具体的数据实现。
开发老手的解读是:Clean Architecture 不是为了分层而分层,而是为了保护核心业务逻辑不被外界变化影响。
框架会迭代、库会替换、UI 范式会变迁,但业务规则(可以理解为 UE 定义的逻辑)往往是稳定的------Clean Architecture 就是要把这份稳定性隔离出来。
业务规则...稳定吗?
上面这段话,可能很多开发者会觉得:业务规则是不稳定,可能我的库一辈子不会替换,但是产品逻辑一直在变换。
这里的业务规则稳定,其实是另一种概念,我举个例子,假设产品设计了一个产品,当开发评估实现过程的时候,可能考虑到了 IOS,Windows,Web,Android 等不同平台,针对这些不同平台,它们的业务逻辑是统一的,但是到研发手里,UI,数据存储等这些技术细节就是变化的。
即使只考虑 Android,我们也会遇到很多 Bug,这些 Bug 也会让技术细节有特别多的改动,因为技术细节就是为产品服务的,其主要目的就是为了实现产品逻辑的稳定。
3. 现代 Android 应用中,业务逻辑该放在哪里
嘿,这个看似简单的问题,最能体现架构成熟度,也最能体现资深与普通开发者的差距!
初级开发者常把业务逻辑写在 Activity 或 Fragment 里;稍进阶一点的,会把所有逻辑都移到 ViewModel。但这两种做法都有隐患------它们把业务规则和 Android 生命周期类强绑定,后续维护只会越来越难。
如果是个人的 Demo 项目,为了简单的解决一个小问题,后续没有什么迭代更新,这么做是没有任何问题的 ------ 够快才能抓住机会!
而开发老手的选择是:引入用例(UseCase)。每个类对应一个独立的业务操作,比如CalculateShippingCostUseCase(计算运费)、SubmitLoginUseCase(提交登录),把验证规则、仓库调用、结果处理都封装在里面。
ViewModel 只负责调用用例,再把结果以 UI 状态的形式暴露给视图。
这样做的好处很明显:业务逻辑脱离 Android 依赖,可独立测试;能在多个页面复用;ViewModel 也能专注于 UI 协调,不用操心领域逻辑。
一个实用的判断标准:如果明天 Android 框架消失了,你的业务逻辑依然能独立运行、逻辑自洽,那它的位置就对了;反之,就是放错了地方。
仔细想想,UseCase 大部分代码和 Android 是没有关系的,它有自己的输入以及针对 UI 状态的输出。
4. 什么时候该从单模块应用迁移到多模块架构

早期应用适合单模块结构------搭建快、理解成本低,小团队用着刚刚好。但随着代码库扩大,问题会逐渐暴露:构建时间越来越长,不相关功能耦合越来越紧,新开发者上手越来越难。
多模块架构的核心价值是"划清边界":可以把功能拆成独立模块,核心工具、设计系统、网络层、领域逻辑则放在共享模块中。这样能缩小编译范围,明确代码所有权,避免团队协作时互相干扰。
开发老手不会为了"显得高级"而盲目模块化。
只有当出现明确痛点------比如构建太慢、团队协作冲突频繁、需要隔离功能所有权时,模块化才是合理选择。否则,过早模块化只会增加不必要的复杂度。
架构决策从来不是选"理论上最好的",而是选"能解决当前瓶颈的",即局部最优解即可!
5. 仓库(Repository)的真正作用是什么
很多项目里的 Repository,只是 API 服务的"薄包装"------调用 Retrofit 返回结果,顶多再加个数据库查询。这种表层实现,完全浪费了仓库的架构价值。
仓库的核心职责是"抽象数据源",给应用其他部分提供统一接口。它要决定数据来自缓存、网络还是本地存储,可能还要组合多个数据源、做数据转换、执行一致性规则。
举个例子:仓库可以先返回缓存数据,保证 UI 响应迅速,同时后台从网络拉取最新数据并更新缓存。而 ViewModel 完全不用关心这些细节,只要观察仓库的输出就行。
资深开发者把仓库当成"决策者",而不是"传递者"。如果一个仓库只是简单转发请求,那它其实没有很大必要存在。
6. Compose 如何改变架构思维

Compose 的核心变革,是把 UI 开发从"命令式更新"变成了"声明式渲染"。
开发者不用一步步告诉视图该做什么,而是描述"特定状态下 UI 该长什么样"。
这一变革让"状态管理"成为架构核心------UI 变成了状态的函数。如果状态可预测、不可变,UI 的表现也会变得可预测。
开发老手们通常为每个独立的 UI 功能定义一个单一的 UI 状态对象(通常使用 data class),包含所有必要属性。
状态变化会触发重组,事件从 UI 向上传递,状态从 ViewModel 向下流动,形成单向数据流。
现代化的 UI 开发,一句话讲:状态导致 UI 变化,UI 通过事件导致状态变化!
Compose 不强制要求新架构,但会更快暴露旧架构的缺陷。如果团队无视单向数据流,手动触发 UI 更新或在多个地方存储可变状态, bug 会很快找上门。
架构必须为屏幕状态提供"单一数据源",才能适配 Compose 的开发模式。
7. 2026年了,该用 Flow 还是 LiveData
其实这个问题,在三年前就应该下结论------Flow 一把梭哈!
LiveData 在早期 Android 架构中功不可没,但如今 Kotlin Flow 已经成为更灵活、更强大的响应式组件。
Flow 与协程无缝集成,支持丰富的转换操作符,还能在应用所有层通用。和 LiveData 不同,它默认不绑定 Android 生命周期,更适合用在领域层和数据层。
开发老手的实践是:内部用 Flow 传递数据,在 UI 边界处用生命周期感知的作用域收集数据。
在 Compose 中,以生命周期感知方式收集 Flow 已经变得非常简单(一个函数搞定------collectAsState)。
这个选择不是要弃用 LiveData,而是要选一个能在整个架构中支持异步管道的流抽象------Flow 显然更胜任这个角色。
当然,选择 Flow 还有另一个很重要的原因:Flow 是纯 Kotlin 实现的,这是跨平台开发的唯一选择。
毕竟,夫人......哦不是,开发者们,你也不想再跨平台的时候,重新写一遍数据流吧!
8. 如何防止 ViewModel 变得臃肿
这是 Android 应用中最常见的架构问题之一。
在 #1 的问题中,提到过。
ViewModel 容易膨胀,核心是因为它太"好用"了:能存活于配置变更、可访问协程、处于 UI 和数据之间的关键位置。久而久之,各种职责都会堆过来,最后变得臃肿不堪。
开发老手的应对方法是"主动向外剥离逻辑":复杂业务操作移到用例,数据映射移到专门的映射器类,数据组合逻辑放到仓库,导航决策可以封装成事件,由导航处理器处理。
一个健康的 ViewModel,读起来应该像"状态协调器";如果读起来像"小型后端服务",说明它已经超出了应有的职责范围。
9. 大型 Android 应用的导航该如何设计
小型应用可以直接用 Navigation 组件,inline 定义 Fragment 或 Compose 路由就行。但大型应用的导航逻辑,往往需要更高层次的抽象。
如果 ViewModel 直接调用 UI 框架的导航 API,会造成强耦合。资深团队的做法是:ViewModel 只发送导航意图或事件,UI 层解析这些事件并执行导航操作。
有些团队会引入导航接口或协调器模式,集中管理导航决策。这种方式让导航逻辑可测试,还能避免业务逻辑直接引用 UI 目标页面。
导航看似是 UI 层面的事,但在复杂应用中,它会成为重要的架构边界。
等等,什么是架构边界?
一件事,该谁干------就是边界。
10. 如何处理依赖注入,不让框架主导架构
Hilt 这类框架能减少模板代码,但也容易让人走捷径。如果过度依赖注解和自动注入,依赖关系可能会无意间跨越架构边界。
开发老手的原则是:先设计架构,再把 DI 框架当成"组装工具"。领域层依赖接口而非实现,数据层提供具体实现,UI 层只依赖抽象。
如果切换 DI 框架需要重构整个代码库,说明架构和框架耦合太紧了。
框架应该支持架构设计,而不是定义架构设计。
我个人最喜欢的 DI 框架其实是 Koin。
11. 如何设计离线优先或缓存感知系统

离线支持不该是"事后补充"的功能------它会影响整个应用的数据流转逻辑。
有经验的开发者通常把缓存逻辑放在仓库层:由仓库决定何时返回缓存数据、何时刷新数据、如何合并多源数据。它可能会暴露一个 Flow,先发送缓存数据,再发送更新后的新数据。
UI 完全不用关心数据来自网络还是本地磁盘,只需响应状态变化。这种设计让离线逻辑集中化,避免多个页面重复写缓存代码。
早期就为不稳定网络设计架构,能省去后期大量重构工作。
从这个问题就可以看出,为什么现代化的开发都喜欢使用 Repository 去处理数据,而不是直接调用 Retrofit 接口。
12. 错误该如何在架构各层传递
直接向上层抛出原始异常,虽然简单但很混乱------网络错误、解析失败、数据库问题等技术细节,会泄露到 UI 层。
一个好的系统的做法是:把技术错误转化为领域级错误模型。比如仓库不会抛出 IOException,而是发送DomainError.NetworkUnavailable(网络不可用)或DomainError.Timeout(超时),ViewModel 再把这些领域错误映射成用户友好的提示信息。
这种方式让各层"说自己的语言":数据层处理技术细节,领域层定义业务含义,UI 层负责展示。错误处理从此变得可预测,而不是杂乱无章。
13. 什么时候领域层才是真正必要的
不是所有应用都需要完整的领域层(包含用例和实体)。过早引入领域层会拖慢开发速度,还会增加不必要的间接性。
老手们会观察"信号"再决定是否添加领域层:如果业务规则复杂、需要在多个功能中复用、或者需要脱离 Android 独立测试,领域层就很有价值;如果应用只是简单展示远程数据,几乎不需要数据转换,那更简单的结构就足够了。
好的架构会随产品一起演进,很少能在第一天就设计得尽善尽美(当然也没必要做,提前优化也是一种负优化)。
14. 如何确保架构从一开始就支持测试
测试难度往往能反映架构缺陷。
如果写个单元测试都需要启动 Activity,或者 mock 半个 Android 框架,说明架构边界设计错了。
一个合理的架构,会把业务逻辑隔离在纯 Kotlin 类中,分离接口与实现,核心层避免直接依赖 Android 框架。这样一来,单元测试就能快速、可靠地运行。
这也正是为什么 #7 问题中,推荐使用 Flow 的原因,Flow 不依赖 Android,纯 Kotlin 就能测试!
测试不是在架构设计完成后才添加的,而是关注点分离良好的自然结果。
15. 现在 Android 团队最容易犯的架构错误是什么
最常见的错误是"盲目照搬模式,却不理解其目的"。很多团队照搬 Clean Architecture、创建多个模块、引入复杂的状态容器、添加多层抽象,只因为他们觉得"这才是高级架构该有的样子"。
但不必要的抽象会拖慢开发速度,让新成员困惑。
最好的架构不是分层最多的,而是能清晰分离职责,同时又足够简单、让团队能轻松理解的。
资深开发者优化的是"清晰度、可扩展性和可维护性",而不是用各种模式名称去打动别人。