MVVM实战:从项目重构到架构升级的踩坑与成长
去年我们团队接手了一个已有三年历史的老项目——一款面向用户的健康管理类App。这个App最初由几个实习生用MVC模式快速搭建,随着功能不断增加、开发人员更替频繁,代码逐渐变得难以维护。
在一次需求评审会上,面对一个涉及多页面状态联动的新模块,我意识到如果继续沿用原来的架构,不仅开发效率低,而且容易出错。作为一个已经经历过多个移动项目的技术负责人,我觉得是时候给这个应用进行一次“体检”了。
于是,我们决定将原有架构改为MVVM(Model-View-ViewModel),希望通过这一改变提升代码可读性、解耦能力以及团队协作效率。
项目背景与问题挑战

这款App的核心功能包括:
- 用户健康数据采集与展示
- 医疗建议订阅推送
- 健康社区互动
在开始重构前,我们面临以下几个问题:
- View层臃肿:Activity和Fragment中大量混杂业务逻辑和UI处理。
- 状态管理混乱:不同页面之间共享数据,常常需要层层传递,容易出错。
- 测试困难:由于视图与业务强耦合,单元测试覆盖率极低。
- 多人协作冲突频繁:因为缺乏清晰职责划分,经常出现同一文件被多人修改的问题。
这些问题在迭代过程中越发明显,直接影响了交付质量和上线节奏。作为技术负责人,我意识到必须引入更清晰的架构思想来解决问题,而MVVM正是当时最适合的选择。
技术方案选择与实现思路

我们最终选择了Jetpack MVVM架构组件组合(以Android平台为主),包括:
- ViewModel + LiveData:用于状态持有和通信
- Room数据库:本地数据持久化
- Repository层:统一数据访问接口
- DataBinding / ViewBinding:降低视图绑定复杂度
简单说下MVVM的结构模型:
- View(Activity / Fragment)只负责UI渲染和事件转发
- ViewModel 负责准备和管理数据供View使用,生命周期感知
- Repository 层处理真实的数据来源(网络、数据库等)
- Model 层表示实体数据和部分业务逻辑
这种结构天然支持分层设计和关注点分离,非常适合我们当时的项目现状。
实战中的代码片段
这里分享两个关键组件的代码示例:
1. ViewModel 示例
class HealthDataViewModel : ViewModel() {
private val repository = HealthDataRepository()
// 使用LiveData管理数据
private val _healthData = MutableLiveData<HealthData>()
val healthData: LiveData<HealthData> = _healthData
fun loadUserData(userId: String) {
viewModelScope.launch {
val data = repository.fetchUserData(userId)
_healthData.postValue(data)
}
}
}
2. Repository 层封装数据来源
object HealthDataRepository {
suspend fun fetchUserData(userId: String): HealthData {
val localData = getFromLocalCache(userId)
if (localData != null) return localData
val remoteData = apiService.fetchRemoteData(userId)
saveToLocal(remoteData)
return remoteData
}
private fun getFromLocalCache(userId: String): HealthData? {
// 实现Room或SharedPreferences查询
}
private fun saveToLocal(data: HealthData) {
// 数据库持久化操作
}
}
通过这种方式,我们将数据获取、转换和更新全部集中到了Repository层,极大提升了数据处理逻辑的复用性和可测试性。
踩过的那些坑和解决方法
虽然MVVM听起来很美好,但在实践中我们也遇到了不少实际问题:
1. LiveData更新时机混乱
一开始我们在ViewModel中使用postValue()频繁更新界面,结果导致某些场景下数据丢失或错乱。
解决方案:
- 对高频更新的需求,改用
MediatorLiveData进行合并 - 针对列表型数据,结合DiffUtil优化刷新性能
- 使用
viewModelScope.launch统一异步处理流程
2. 多个ViewModel共享状态不一致
比如用户头像更新后,首页和社区页面没有同步变化。
解决方案:
- 引入Singleton的SharedViewModel来管理跨页面的状态共享
- 使用EventBus做事件广播(谨慎使用,避免滥用)
3. 构建流程变慢
加入ViewBinding和Kotlin协程之后,编译时间一度增加了30%以上。
优化手段:
- 升级Gradle版本(从6.x升到7.x)
- 启用增量注解处理器
- 拆分大模块为feature module
架构升级后的效果
经过两个月的逐步迁移和优化,项目整体质量有了显著改善:
- 开发效率提升:新同学上手更快,页面开发时间平均缩短20%
- Bug减少:状态管理和数据流更加可控,线上Crash率下降约40%
- 可维护性强:模块化程度提高,后续重构成本更低
- 测试覆盖高:ViewModel层可以轻松写单元测试,覆盖率从15%提升到60%
更惊喜的是,在一次客户验收演示中,原本容易卡顿的数据加载页现在变得流畅自然,连非技术人员也察觉到了性能上的变化。
经验总结与建议
作为经历完整重构过程的一员,我想给大家几点忠告:
- 不要为了MVVM而MVVM:如果你的项目很小,或者只有一个人维护,直接上MVVM可能反而增加复杂度。
- 渐进式迁移最稳妥:旧项目不要一次性重构完所有页面,先拿一个小模块练手,建立标准后再推广。
- 规范先行:制定好命名、目录结构、依赖边界等规范,否则MVVM也会变成另一个“面条代码”的温床。
- 注重协同:MVVM适合团队协作,但也需要大家有共同的理解和坚持。
- 灵活应对各平台差异:如果是跨平台或iOS+Android双端都要做,MVVM理念依然适用,但具体实现方式要根据平台特性调整。
写在最后的一点感悟

有时候回头看,架构设计不仅仅是技术决策,更是一种团队文化的体现。MVVM不只是让代码变整洁,它还帮我们建立了更清晰的责任分工意识和沟通方式。很多以前争执不清的“谁改谁负责”问题,现在一看就能定位是谁的责任。
技术这事儿啊,从来不是一锤子买卖。我们今天写的代码,明天可能就要改;但我们今天做的架构选择,也许会影响下一个版本的命运。
所以,永远别怕重构,只要方向正确,过程再痛也能挺过来。当然,最好能早点发现不对劲,而不是等到系统快撑不住才想起来换架构 😅
希望这篇文章能带给你一些启发,也欢迎你留言交流你们团队在移动架构设计上的实践经验和心得。共勉!

评论 0