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. 提供更好的用户体验

相关推荐
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android