ViewModel 的生命周期与数据保持

6.1.1 配置变更数据保持

什么是配置变更

配置变更是指设备配置发生变化,系统会重建 Activity/Fragment:

  • 屏幕旋转
  • 语言切换
  • 深色模式切换
  • 键盘可用性变化

配置变更的问题

kotlin 复制代码
// 传统方式 - 配置变更导致数据丢失
class MainActivity : AppCompatActivity() {
    
    private var userData: User? = null
    private var orderList: List<Order>? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // 从 Intent 或网络加载数据
        userData = loadUserData()
        orderList = loadOrderList()
        
        updateUI(userData, orderList)
    }
    
    // 屏幕旋转时,Activity 重建,数据丢失
    // onCreate 再次执行,需要重新加载数据
}

ViewModel 的解决方案

kotlin 复制代码
// 使用 ViewModel - 配置变更时数据保持
class MainActivity : AppCompatActivity() {
    
    private val viewModel: UserViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // ViewModel 在配置变更时保持不变
        viewModel.userData.observe(this) { user ->
            updateUI(user)
        }
        
        viewModel.orderList.observe(this) { orders ->
            updateUI(orders)
        }
    }
}

class UserViewModel : ViewModel() {
    
    private val _userData = MutableLiveData<User>()
    val userData: LiveData<User> = _userData
    
    private val _orderList = MutableLiveData<List<Order>>()
    val orderList: LiveData<List<Order>> = _orderList
    
    init {
        // 只在 ViewModel 首次创建时加载一次数据
        loadUserData()
        loadOrderList()
    }
    
    private fun loadUserData() {
        viewModelScope.launch {
            val user = userRepository.getUser()
            _userData.value = user
        }
    }
    
    private fun loadOrderList() {
        viewModelScope.launch {
            val orders = orderRepository.getOrders()
            _orderList.value = orders
        }
    }
}

6.1.2 分离视图逻辑与业务逻辑

传统方式的问题

kotlin 复制代码
// 传统方式 - 视图逻辑与业务逻辑耦合
class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // 业务逻辑直接写在 Activity 中
        btnLogin.setOnClickListener {
            val username = etUsername.text.toString()
            val password = etPassword.text.toString()
            
            if (username.isEmpty()) {
                Toast.makeText(this, "用户名不能为空", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }
            
            if (password.isEmpty()) {
                Toast.makeText(this, "密码不能为空", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }
            
            if (password.length < 6) {
                Toast.makeText(this, "密码至少 6 位", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }
            
            // 网络请求
            lifecycleScope.launch {
                try {
                    val result = login(username, password)
                    if (result.success) {
                        Toast.makeText(this@MainActivity, "登录成功", Toast.LENGTH_SHORT).show()
                        navigateToHome()
                    } else {
                        Toast.makeText(this@MainActivity, result.message, Toast.LENGTH_SHORT).show()
                    }
                } catch (e: Exception) {
                    Toast.makeText(this@MainActivity, "登录失败:${e.message}", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }
}

ViewModel 的解决方案

kotlin 复制代码
// 使用 ViewModel - 分离视图逻辑与业务逻辑
class MainActivity : AppCompatActivity() {
    
    private val viewModel: LoginViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // 观察登录状态
        viewModel.loginState.observe(this) { state ->
            when (state) {
                is LoginState.Idle -> {
                    // 初始状态
                }
                is LoginState.Loading -> {
                    showLoading()
                }
                is LoginState.Success -> {
                    hideLoading()
                    navigateToHome()
                }
                is LoginState.Error -> {
                    hideLoading()
                    showError(state.message)
                }
            }
        }
        
        // 视图逻辑
        btnLogin.setOnClickListener {
            val username = etUsername.text.toString()
            val password = etPassword.text.toString()
            viewModel.login(username, password)
        }
    }
}

// ViewModel - 纯业务逻辑
class LoginViewModel(
    private val loginRepository: LoginRepository
) : ViewModel() {
    
    private val _loginState = MutableLiveData<LoginState>()
    val loginState: LiveData<LoginState> = _loginState
    
    fun login(username: String, password: String) {
        // 业务逻辑
        if (username.isEmpty()) {
            _loginState.value = LoginState.Error("用户名不能为空")
            return
        }
        
        if (password.isEmpty()) {
            _loginState.value = LoginState.Error("密码不能为空")
            return
        }
        
        if (password.length < 6) {
            _loginState.value = LoginState.Error("密码至少 6 位")
            return
        }
        
        // 网络请求
        viewModelScope.launch {
            _loginState.value = LoginState.Loading
            try {
                val result = loginRepository.login(username, password)
                if (result.success) {
                    _loginState.value = LoginState.Success(result.user)
                } else {
                    _loginState.value = LoginState.Error(result.message)
                }
            } catch (e: Exception) {
                _loginState.value = LoginState.Error("登录失败:${e.message}")
            }
        }
    }
}

// 登录状态
sealed class LoginState {
    object Idle : LoginState()
    object Loading : LoginState()
    data class Success(val user: User) : LoginState()
    data class Error(val message: String) : LoginState()
}

6.1.3 ViewModel 的生命周期

ViewModel 的生命周期示意图

复制代码
Activity 生命周期:
onCreate → ViewModel 创建
onStart
onResume
onPause
onStop
onDestroy (配置变更) → ViewModel 保持
onCreate → ViewModel 复用
onDestroy (真正销毁) → ViewModel 销毁

Fragment 生命周期:
onAttach → ViewModel 创建
onCreate
onCreateView
onStart
onResume
onPause
onStop
onDestroyView → ViewModel 保持
onDestroy (配置变更) → ViewModel 保持
onDetach → ViewModel 销毁

ViewModel 生命周期的特点

特性 说明
创建时机 Activity/Fragment 首次创建时
销毁时机 Activity/Fragment 真正销毁时(非配置变更)
配置变更 配置变更时 ViewModel 保持不变
范围 Activity/Fragment 的生命周期范围

相关推荐
亚空间仓鼠6 小时前
Docker容器化高可用架构部署方案(八)
android·docker·架构
恋猫de小郭6 小时前
2026 Android I/O ,全新 AI 手机、 Android PC 和自动驾驶
android·人工智能·智能手机
三产7 小时前
Hermes 教程 03:Skills 系统
android·java·数据库
AI玫瑰助手7 小时前
Python流程控制:for循环遍历字符串列表字典
android·java·python
2501_915918417 小时前
iOS性能数据监控:从概念到工具实践,让应用运行更流畅
android·macos·ios·小程序·uni-app·cocoa·iphone
帅次7 小时前
测试分层:JVM 单测、ViewModel 测试与 Compose UI Test
android·jvm·ui·kotlin·compose·modifier
背包客(wyq)7 小时前
开源中文语音模型Android端部署测试
android·开源
yewq-cn7 小时前
Android Compatibility
android
马占柱7 小时前
MediaSession相关介绍
android·多媒体·mediasession·车载多媒体开发
张风捷特烈7 小时前
状态管理大乱斗#08 | MobX 源码评析 - 透明魔法
android·前端·flutter