0赞
赞赏
更多好文
上周,我被拉去救火一个老Android项目。打开Activity代码,一眼望去全是逻辑:网络请求、数据处理、UI更新……像一锅乱炖。改个按钮颜色,得翻遍整个类。当时就琢磨:这玩意儿得重构了,不然团队要集体崩溃。
后来我们上了MVVM,不是为了追时髦,而是真刀真枪地拆解了代码。今天不扯理论,说说怎么在真实项目里落地MVVM,避免踩坑。
为什么MVVM能救命?先看痛点
传统写法(MVC/MVP)的坑,你肯定熟:
// 伪代码:Activity里塞满业务逻辑
public class UserActivity extends AppCompatActivity {
private TextView nameText;
@Override
protected void onCreate(Bundle savedInstanceState) {
nameText = findViewById(R.id.name);
// 1. 网络请求
new Thread(() -> {
User user = api.getUser();
// 2. 数据处理
String formatted = user.getName().toUpperCase();
// 3. UI更新(必须在主线程)
runOnUiThread(() -> nameText.setText(formatted));
}).start();
}
}
问题:
- 业务逻辑和UI混在一起,改个需求得动多处
- 网络请求没生命周期管理,Activity销毁后还在跑
- 测试?别提了,得写Instrumentation测试
MVVM的核心是:ViewModel管数据,View只负责展示。ViewModel和Activity生命周期绑定,数据变自动更新UI。
工程化落地:别照搬教程,要真用起来
1. 核心组件怎么用?别堆砌
-
ViewModel:数据容器,生命周期感知
class UserViewModel : ViewModel() { val user = MutableLiveData<User>() // 用LiveData自动通知UI fun loadUser() { // 真实项目:用Repository处理网络 UserRepository.loadUser { user -> this.user.value = user } } }关键点:ViewModel不持有View引用,避免内存泄漏。别在ViewModel里写
context或Activity。 -
Data Binding:XML直接绑定数据,少写
findViewById<!-- layout/user_activity.xml --> <TextView android:text="@{viewModel.user.name}" ... />为什么不用? 有些人嫌麻烦,但实际用起来:
- 100行
findViewById→ 0行 - UI改动时,不用改Activity代码(比如改TextView颜色,只改XML)
- 100行
2. 工程化关键:目录结构和规范
别把ViewModel塞在Activity同级目录!我们定的规范:
src/main/java/com/example/app
├── viewmodel/ # ViewModel放这里
│ └── UserViewModel.kt
├── repository/ # 网络/数据库逻辑
│ └── UserRepository.kt
├── ui/ # Activity/Fragment
│ └── UserActivity.kt
└── binding/ # Data Binding的BindingAdapter(比如自定义转换)
为什么重要?
- 团队协作时,前端改UI、后端改数据,互不干扰
- 重构时,直接定位到
viewmodel/,不用翻Activity
3. 避坑指南:我们踩过的坑
-
坑1:滥用LiveData
问题:在ViewModel里直接observe,导致Activity销毁后还回调。
解法:用lifecycleScope启动协程,或在Activity里observe时绑定生命周期:// UserActivity.kt viewModel.user.observe(this, Observer { user -> nameText.text = user.name })经验:
LiveData是好东西,但别在ViewModel里用observe。 -
坑2:ViewModel太重
问题:把网络请求、数据库操作全塞进ViewModel。
解法:Repository是数据中转站。ViewModel只调用Repository,不处理具体逻辑:// UserViewModel.kt fun loadUser() = userRepository.loadUser() // 专注数据流转 -
坑3:Data Binding过度使用
问题:所有UI事件都用BindingAdapter,代码变臃肿。
解法:简单场景用setOnClickListener,复杂逻辑用BindingAdapter:<Button android:onClick="@{() -> viewModel.onLoginClick()}" />
实际效果:我们团队的改变
- 代码量:老项目重构后,Activity平均从500+行→150行
- 提测效率:修复UI相关Bug时间减少40%(因为业务逻辑和UI分离)
- 新人上手:新成员能快速定位问题(比如改数据逻辑,直接看
repository/)
“一开始觉得MVVM是套壳,现在用习惯了,写Activity像写纯UI——数据都是ViewModel给的。”
—— 团队后端同学
最后说点实在的
MVVM不是银弹。别为了用而用:
- 小功能(比如一个计数器页面)直接写Activity也行
- 但中大型项目,不拆MVVM=埋雷
我们团队现在定死:新功能必须用MVVM,老代码逐步重构。别追求100%完美,先从一个Activity开始。你试试看,你会回来感谢我。
