一、LiveData与ViewModel:不只是会用,更要懂原则

说实话,LiveData和ViewModel这对组合,是Android开发中最常用的架构组件之一。但我在做技术评审时发现,很多人只是「会用」,却不知道背后的设计原则。今天我就把这几年的实战经验梳理一下,聊聊最佳实践。

核心原则:ViewModel负责状态持有,LiveData负责数据观察。两者各司其职,不要越界。

1.1 架构原则:谁该做什么事?

我见过不少项目,把网络请求直接写在ViewModel里,或者把UI逻辑塞进LiveData的观察回调中。这些都是典型的架构混乱。

正确的分工应该是这样的:

  • ViewModel:管理UI相关的数据,处理业务逻辑,但不直接操作UI
  • LiveData:作为数据容器,通知观察者数据变化,但不做任何业务判断
  • Repository:数据来源的统一入口,ViewModel只跟Repository打交道
// 好的做法:ViewModel只关心数据,不关心UI
class UserViewModel(private val repo: UserRepository) : ViewModel() {
    private val _user = MutableLiveData<User>()
    val user: LiveData<User> get() = _user

    fun loadUser(userId: String) {
        viewModelScope.launch {
            val result = repo.fetchUser(userId)
            _user.value = result
        }
    }
}

注意:不要在ViewModel中持有Activity或Fragment的引用。我曾经见过一个项目,ViewModel里存了TextView的引用,结果内存泄漏查了两天。

1.2 代码规范:命名与结构

我个人习惯用下划线前缀表示私有LiveData,对外暴露不可变的版本。这个约定在Google官方示例中也有体现,但很多团队并不遵守。

// 命名规范示例
class ProfileViewModel : ViewModel() {
    private val _name = MutableLiveData<String>()  // 私有可变
    val name: LiveData<String> get() = _name       // 公开不可变

    private val _age = MutableLiveData<Int>()
    val age: LiveData<Int> get() = _age
}

你想想看,如果直接把MutableLiveData暴露出去,外部代码就能随意修改数据,这跟全局变量有什么区别?

1.3 常见反模式:这些坑我踩过

做架构评审这几年,我总结了几种最常见的反模式。嗯,这里要重点说说。

反模式 问题描述 正确做法
LiveData事件处理 用LiveData传递一次性事件(如Snackbar) 使用SingleLiveEvent或Channel
ViewModel持有Context 在ViewModel中保存Activity引用 使用ApplicationContext或AndroidViewModel
过度使用LiveData 每个字段都变成LiveData 合并相关数据,使用Data Class
忘记取消协程 在ViewModel中启动协程但不管理生命周期 使用viewModelScope

避坑指南:我曾经在一个电商项目中,用LiveData传递「加入购物车成功」的事件。结果屏幕旋转后,事件被重复触发,用户被加了两次商品。后来改用SingleLiveEvent才解决。

1.4 性能优化:别让LiveData拖慢你的App

LiveData本身很轻量,但用不好也会带来性能问题。我总结了几条优化建议:

  • 避免频繁触发:如果数据变化很频繁(比如传感器数据),考虑使用Flow或做防抖处理
  • 合理使用Transformations:map和switchMap虽然方便,但每次数据变化都会重新计算,注意不要在里面做耗时操作
  • 及时移除观察者:虽然LiveData会自动清理,但如果你手动添加了观察者,记得在适当的时候移除
// 性能优化示例:使用MediatorLiveData合并多个数据源
class DashboardViewModel : ViewModel() {
    private val userLiveData = MutableLiveData<User>()
    private val settingsLiveData = MutableLiveData<Settings>()

    val dashboardData = MediatorLiveData<DashboardUiState>().apply {
        addSource(userLiveData) { user ->
            value = combine(user, settingsLiveData.value)
        }
        addSource(settingsLiveData) { settings ->
            value = combine(userLiveData.value, settings)
        }
    }
}

说白了,性能优化的核心就一句话:减少不必要的计算和通知。你想想看,如果一个数据变化了,但UI不需要更新,那为什么要通知观察者呢?

1.5 知识体系总览

下面这张图是我整理的LiveData与ViewModel最佳实践的知识结构,你可以对照着检查自己的项目:

LiveData + ViewModel 架构原则 • ViewModel持有状态 • LiveData通知变化 • Repository统一数据源 代码规范 • 私有MutableLiveData • 公开不可变LiveData • 统一命名约定 常见反模式 • LiveData传递事件 • ViewModel持有Context • 过度使用LiveData 性能优化 • 避免频繁触发 • 合理使用Transformations • 及时移除观察者 核心:各司其职,减少耦合

这张图把四个核心维度都列出来了。我个人建议你把它打印出来贴在工位上,写代码的时候对照着看,能避免很多低级错误。

总结一下:LiveData和ViewModel本身不难,难的是怎么在团队中统一规范、避免踩坑。记住三个关键词:职责单一、命名规范、性能意识。做到这三点,你的架构至少能打80分。

公众号:蓝海资料掘金营,微信deep3321