ViewModel + LiveData 教程:构建响应式 Android 应用
ViewModel
和 LiveData
是 Android Jetpack 架构组件的核心模块,用于构建响应式 、生命周期安全的现代 Android 应用。以下是完整实现指南:
📌 核心概念
组件 | 作用 |
---|---|
ViewModel |
以生命周期感知方式管理 UI 数据,界面旋转时数据不丢失 |
LiveData |
可观察的数据容器,仅在 Activity/Fragment 处于活跃状态时更新 UI |
优势 | ✅ 避免内存泄漏 ✅ 自动更新 UI ✅ 数据持久化 ⚡️ 响应式编程 |
🛠️ 配置 Gradle
首先在 app/build.gradle
添加依赖:
bash
dependencies {
def lifecycle_version = "2.6.2"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
}
🧩 步骤 1: 创建 ViewModel
kotlin
// CounterViewModel.kt
import androidx.lifecycle.ViewModel
import androidx.lifecycle.MutableLiveData
class CounterViewModel : ViewModel() {
// MutableLiveData 用于修改数据,对外暴露不可变 LiveData
private val _counter = MutableLiveData(0)
val counter: LiveData<Int> get() = _counter
// 在后台线程模拟耗时操作
fun increment() {
viewModelScope.launch(Dispatchers.Default) {
delay(1000) // 模拟耗时操作
_counter.postValue(_counter.value?.plus(1))
}
}
// 普通数据操作(无耗时)
fun reset() {
_counter.value = 0
}
}
🖼 步骤 2: 在 Activity/Fragment 中使用
kotlin
// MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val viewModel: CounterViewModel by viewModels() // 委托初始化
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 监听 LiveData 变化(仅当 Activity 处于 STARTED/RESUMED 状态时触发)
viewModel.counter.observe(this) { count ->
binding.tvCount.text = "Count: $count"
binding.progressBar.visibility = if (count > 5) View.VISIBLE else View.GONE
}
binding.btnIncrement.setOnClickListener {
viewModel.increment()
}
binding.btnReset.setOnClickListener {
viewModel.reset()
}
}
}
📐 布局文件 (activity_main.xml)
ini
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tvCount"
android:text="Count: 0"
android:textSize="24sp"/>
<ProgressBar
android:id="@+id/progressBar"
android:visibility="gone" />
<Button
android:id="@+id/btnIncrement"
android:text="Increment after delay"/>
<Button
android:id="@+id/btnReset"
android:text="Reset"/>
</LinearLayout>
⚡ 高级用法
1. 数据转换 (Transformations)
kotlin
class UserViewModel : ViewModel() {
private val _userId = MutableLiveData("")
val userName: LiveData<String> = Transformations.map(_userId) { id ->
repository.getUserName(id) // 将用户ID转换成用户名
}
}
2. 多 Fragment 共享数据
csharp
// 在 Fragment 中共享同一个 ViewModel
val sharedViewModel: SharedViewModel by activityViewModels()
3. 结合 Room 数据库
kotlin
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getAllUsers(): LiveData<List<User>> // Room 原生支持 LiveData
}
// ViewModel 中直接暴露查询结果
val users: LiveData<List<User>> = userDao.getAllUsers()
🚫 常见错误及解决方案
问题 | 解决方案 |
---|---|
直接在 LiveData 中更新 UI | 通过观察者模式更新,避免在 ViewModel 操作 UI |
忘记调用 observe() |
确保所有 LiveData 都被观察 |
后台线程错误使用 .value= |
用 postValue() 从非 UI 线程更新 |
ViewModel 持有 Context 引用 | 使用 AndroidViewModel + Application 上下文 |
🔐 生命周期安全原理
- ViewModel 通过
ViewModelStore
在配置更改时存活 - LiveData 自动取消订阅非活跃状态组件
viewModelScope
在onCleared()
自动取消协程
💡 最佳实践
- 职责分离:ViewModel 只负责数据处理,不涉及 UI 逻辑
- 单向数据流:View → ViewModel → Repository → DataSource
- 状态管理 :使用
StateFlow
或MediatorLiveData
合并多个数据源 - 测试:独立测试 ViewModel 和 LiveData(无需 Android 依赖)
掌握 ViewModel + LiveData 可大幅提升应用稳定性与响应效率!建议结合 Data Binding
或 Kotlin Flow
构建更现代化的解决方案。