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

相关推荐
好的佩奇8 小时前
蓝牙之通信架构
android·flutter·蓝牙
FlightYe8 小时前
视界原理之3D视频(四):裸眼、眼镜和头显
android·linux·c++·3d·ffmpeg·音视频
诸神黄昏EX9 小时前
Android fw BatteryService
android
alexhilton9 小时前
提升 Android Compose 页面的流畅度
android·kotlin·android jetpack
撩得Android一次心动11 小时前
Android中Gradle网络下载超时、更改存放位置(解决方案)
android·gradle
HQL_seven12 小时前
【Activity】Android Activity详解
android
AI2中文网12 小时前
安卓 15/16 手机软键盘遮挡输入框、布局顶不上去?已解决:重新编译即可
android·智能手机
字节暗面13 小时前
Android APP 加固横评记录:爱加密、梆梆、网易易盾与腾讯 ACE
android·逆向工程·加固
落魄Android在线炒饭13 小时前
Android 实战问题反推 OpenGL 概念(上)
android·opengl
booboosui13 小时前
DeepSeek 的 Worker 调得动 GitHub,调不动微信:三类 Worker 的实现差别
android