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