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

相关推荐
btown5 小时前
Kotlin 版 MyBatis-Plus 查询优雅解决方案
kotlin
壮哥_icon5 小时前
【Android 系统开发】使用 BAT 脚本高效自动化管理 /system/priv-app/ 系统应用(安装与卸载)
android·运维·自动化
用户69371750013847 小时前
Claude Code终端日志Token占用实测:一个过滤器砍掉60%-90%
android·前端·后端
蝉蜕日记7 小时前
AccumuPDF高级版 v2.57 | 视图文转换PDF,合并分割压缩
android·智能手机·pdf·生活·软件需求
吐了啊取名字太难7 小时前
美颜系统AI修图本地跑并支持Mac、win、安卓、iOS不卡顿
android·人工智能·windows·数码相机·mac·ai编程
阿pin9 小时前
Android随笔-Retrofit
android·retrofit
想取一个与众不同的名字好难9 小时前
安卓自定义颜色选择器
android
我命由我123459 小时前
Android 开发问题:java.lang.NoSuchMethodError:No virtual method readAllBytes()
android·java·java-ee·kotlin·android studio·android-studio·android runtime
Coffeeee9 小时前
ios零基础的Android开发能否靠AI让老板省一笔人工费呢
android·ios·ai编程
Joey_friends9 小时前
指纹authenticate流程图
android·java·c++