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 的生命周期范围

相关推荐
凛_Lin~~2 小时前
安卓实现textview跑马灯效果
android·java
Fate_I_C3 小时前
Kotlin函数一
android·开发语言·kotlin
我讲个笑话你可别哭啊3 小时前
Android Studio无线调试连接安卓设备
android·ide·android studio
pengyu3 小时前
【Kotlin 协程修仙录 · 炼气境 · 初阶】 | 感受天地灵气,写出第一个挂起函数
android·kotlin
林栩link3 小时前
Android CLI 与 Skills:提升 AI Coding 效率
android
AI玫瑰助手4 小时前
Python基础:列表的定义、增删改查核心操作
android·开发语言·python
AirDroid_cn4 小时前
安卓15分享Wi-Fi二维码能换颜色吗?自定义颜色方法
android
儿歌八万首4 小时前
Compose 自定义组件:封装一个通用标题栏
android·compose·标题栏
ZHOUPUYU5 小时前
PHP性能优化实战:提升你的应用速度
android·性能优化·php