Android ViewModel 深度解析:原理、使用与最佳实践

一、ViewModel 概述

ViewModel 是 Android Jetpack 架构组件中的重要一员,专门为解决 Activity 和 Fragment 中的 UI 数据管理问题而设计。它的核心目标是:

  1. 管理 UI 相关数据:以生命周期感知的方式保存和管理数据

  2. 解决配置变更问题:在屏幕旋转等配置更改时保留数据

  3. 避免内存泄漏:自动清理资源,防止 Activity/Fragment 引用泄漏

    复制代码
    // 基本ViewModel类定义
    class MyViewModel : ViewModel() {
        // 数据将在此保存
        var counter = 0
    }

    二、ViewModel 生命周期

    理解 ViewModel 的生命周期是其正确使用的关键:

  • ViewModel 的生命周期比创建它的 Activity/Fragment 更长

  • 在 Activity 完成(finish)时才会清除

  • 屏幕旋转等配置变化不会导致重建

    复制代码
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            // 获取ViewModel实例
            val model: MyViewModel by viewModels()
        }
    }

    三、ViewModel 的基本使用

    1. 添加依赖

    首先在 build.gradle 中添加依赖:

    复制代码
    dependencies {
        implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2"
        // 如果使用ViewModel带SavedState
        implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2"
    }

    2. 创建 ViewModel 类

    复制代码
    class UserViewModel : ViewModel() {
        private val _users = MutableLiveData<List<User>>()
        val users: LiveData<List<User>> = _users
        
        init {
            loadUsers()
        }
        
        private fun loadUsers() {
            // 模拟数据加载
            _users.value = listOf(User("张三"), User("李四"))
        }
    }

    3. 在 Activity/Fragment 中使用

    class UserActivity : AppCompatActivity() {
    private val userViewModel: UserViewModel by viewModels()

    复制代码
      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_user)
          
          userViewModel.users.observe(this) { users ->
              // 更新UI
              updateUserList(users)
          }
      }

    }

四、ViewModel 的高级特性

1. ViewModel 带参数

如果需要传递参数给 ViewModel,可以使用 ViewModelProvider.Factory:

复制代码
class UserViewModel(private val userId: String) : ViewModel() {
    // ...
}

class UserViewModelFactory(private val userId: String) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return UserViewModel(userId) as T
    }
}

// 使用
val factory = UserViewModelFactory("123")
val viewModel = ViewModelProvider(this, factory).get(UserViewModel::class.java)

2. SavedStateHandle

处理进程死亡后恢复数据:

复制代码
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() {
    val counter: LiveData<Int> = state.getLiveData("counter", 0)
    
    fun increment() {
        state["counter"] = (counter.value ?: 0) + 1
    }
}

3. 在 Fragment 间共享数据

复制代码
class SharedViewModel : ViewModel() {
    val selectedItem = MutableLiveData<Item>()
    
    fun select(item: Item) {
        selectedItem.value = item
    }
}

// FragmentA
val model: SharedViewModel by activityViewModels()

// FragmentB
val model: SharedViewModel by activityViewModels()

五、ViewModel 最佳实践

随着 Jetpack 组件的不断演进,ViewModel 也在持续增强功能(如与 Hilt 的集成、更完善的状态保存机制等),值得开发者持续关注和学习。

  1. 职责单一:每个 ViewModel 应只负责一个屏幕或功能模块

  2. 避免引用 Context:ViewModel 不应持有 Activity/Fragment 的引用

  3. 合理使用 LiveData:暴露不可变的 LiveData,内部使用 MutableLiveData

  4. 结合 Repository:数据操作应委托给 Repository 层

  5. 测试友好:ViewModel 应易于单元测试

    复制代码
    // 良好结构的ViewModel示例
    class OrderViewModel(
        private val orderRepository: OrderRepository,
        private val savedStateHandle: SavedStateHandle
    ) : ViewModel() {
        
        private val _order = MutableLiveData<Order>()
        val order: LiveData<Order> = _order
        
        private val _loading = MutableLiveData<Boolean>()
        val loading: LiveData<Boolean> = _loading
        
        fun loadOrder(orderId: String) {
            _loading.value = true
            viewModelScope.launch {
                try {
                    _order.value = orderRepository.getOrder(orderId)
                } catch (e: Exception) {
                    // 处理错误
                } finally {
                    _loading.value = false
                }
            }
        }
    }

    六、常见问题与解决方案

    1. ViewModel 内存泄漏

    问题 :在 ViewModel 中持有 Activity/Fragment 引用
    解决:使用 Application Context 或完全避免 Context

    2. 数据重复加载

    问题 :每次配置变更都重新加载数据
    解决:在 ViewModel 中缓存数据

    复制代码
    class MyViewModel : ViewModel() {
        private var cachedData: List<Data>? = null
        
        fun getData(): LiveData<List<Data>> {
            if (cachedData == null) {
                loadData()
            }
            return Transformations.map(_source) { it }
        }
    }

    3. 测试困难

    解决:依赖注入和接口抽象

    复制代码
    class MyViewModel(
        private val dataSource: DataSourceInterface
    ) : ViewModel() {
        // ...
    }
    
    // 测试时可以传入Mock实现

    七、ViewModel 与协程

    ViewModel 内置了 viewModelScope,便于协程管理:

    复制代码
    class CoroutineViewModel : ViewModel() {
        fun fetchData() {
            viewModelScope.launch {
                try {
                    val data = repository.fetchData()
                    _uiState.value = UiState.Success(data)
                } catch (e: Exception) {
                    _uiState.value = UiState.Error(e)
                }
            }
        }
    }

    结语

    ViewModel 是现代 Android 开发中不可或缺的架构组件,它优雅地解决了 UI 控制器(Activity/Fragment)中数据管理的难题。通过合理使用 ViewModel,开发者可以:

  6. 构建更健壮、更易维护的应用程序

  7. 提高代码的可测试性

  8. 减少内存泄漏的风险

  9. 提供更好的用户体验

相关推荐
游戏开发爱好者822 分钟前
iOS WebView 远程调试实战 解决表单输入被键盘遮挡和焦点丢失问题
android·ios·小程序·https·uni-app·iphone·webview
没有用的阿吉40 分钟前
adb 指令大全
android·adb调试
amy_jork1 小时前
android studio打包vue
android·vue.js·android studio
编程乐学1 小时前
网络资源模板--基于Android Studio 实现的校园心里咨询预约App
android·android studio·预约系统·大作业·移动端开发·安卓移动开发·心理咨询预约
涵涵子RUSH1 小时前
android studio(NewsApiDemo)100%kotlin
android·kotlin·android studio
九鼎创展科技1 小时前
直播一体机技术方案解析:基于RK3588S的硬件架构特性
android·嵌入式硬件·硬件架构
峥嵘life3 小时前
Android14 锁屏密码修改为至少6位
android·安全
2501_9159184110 小时前
iOS WebView 调试实战 localStorage 与 sessionStorage 同步问题全流程排查
android·ios·小程序·https·uni-app·iphone·webview
Digitally11 小时前
如何永久删除安卓设备中的照片(已验证)
android·gitee
hmywillstronger12 小时前
【Settlement】P1:整理GH中的矩形GRID角点到EXCEL中
android·excel