LiveData 长期以来是 Android 架构组件中状态管理的核心,但随着 Kotlin Flow 的成熟,Google 官方推荐将现有 LiveData 迁移到 Flow。本教程基于官方文章并扩展实践细节,完成平滑迁移。
原 LiveData 代码:
class MyViewModel : ViewModel() {
private val _userName = MutableLiveData("")
val userName: LiveData<String> = _userName
fun updateName(name: String) {
_userName.value = name
}
}
迁移为 StateFlow:
class MyViewModel : ViewModel() {
private val _userName = MutableStateFlow("")
val userName: StateFlow<String> = _userName.asStateFlow()
fun updateName(name: String) {
_userName.value = name
}
}
Activity/Fragment 中收集:
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.userName.collect { name ->
binding.textView.text = name
}
}
}
关键点:使用
repeatOnLifecycle
确保只在 UI 可见时收集,避免资源浪费
LiveData 方式:
val userName: LiveData<String> = Transformations.map(_userName) {
"Hello, $it!"
}
Flow 方式:
val userName: Flow<String> = _userName.map {
"Hello, $it!"
}
LiveData 方式:
val result = MediatorLiveData<String>().apply {
addSource(liveData1) { value = "$it + ${liveData2.value}" }
addSource(liveData2) { value = "${liveData1.value} + $it" }
}
Flow 方式:
val result = combine(flow1, flow2) { data1, data2 ->
"$data1 + $data2"
}
LiveData 常被滥用处理一次性事件(如 Toast、导航),Flow 提供了更专业的解决方案:
class EventViewModel : ViewModel() {
private val _events = MutableSharedFlow<Event>()
val events = _events.asSharedFlow()
sealed class Event {
data class ShowToast(val message: String) : Event()
object NavigateToNext : Event()
}
fun triggerToast() {
viewModelScope.launch {
_events.emit(Event.ShowToast("Hello Flow!"))
}
}
}
UI 层收集:
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.events.collect { event ->
when (event) {
is EventViewModel.Event.ShowToast ->
Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
EventViewModel.Event.NavigateToNext ->
startActivity(Intent(this, NextActivity::class.java))
}
}
}
}
@Dao
interface UserDao {
@Query("SELECT * FROM user")
fun getUsers(): LiveData<List<User>>
}
@Dao
interface UserDao {
@Query("SELECT * FROM user")
fun getUsers(): Flow<List<User>>
}
ViewModel 中使用:
val users: StateFlow<List<User>> = userDao.getUsers()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = emptyList()
)
asLiveData()
临时兼容val userFlow: Flow<User> = repository.getUserFlow()
// 临时保持 LiveData 接口
val userLiveData: LiveData<User> = userFlow.asLiveData()
class HybridViewModel : ViewModel() {
// 新功能使用 Flow
private val _newFeatureState = MutableStateFlow("")
val newFeatureState: StateFlow<String> = _newFeatureState.asStateFlow()
// 旧功能暂保持 LiveData
private val _oldFeatureData = MutableLiveData(0)
val oldFeatureData: LiveData<Int> = _oldFeatureData
}
@Test
fun testLiveData() {
val liveData = MutableLiveData("test")
assertEquals("test", liveData.value)
}
@Test
fun testFlow() = runTest {
val flow = MutableStateFlow("test")
val results = mutableListOf<String>()
val job = launch {
flow.collect { results.add(it) }
}
flow.value = "new value"
assertEquals(listOf("test", "new value"), results)
job.cancel()
}
使用 stateIn
共享流:
val sharedFlow = someFlow
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = null
)
避免重复创建 Flow:
// 错误方式 - 每次调用都创建新流
fun getUser() = userDao.getUserFlow()
// 正确方式 - 共享同一个流
private val _userFlow = userDao.getUserFlow()
val userFlow = _userFlow
合理选择背压策略:
// 缓冲最新值
val events = MutableSharedFlow<Event>(
replay = 0,
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
// 只有在此块内会激活收集
viewModel.data.collect { ... }
}
}
检查:
// 暴露 LiveData 给 Java 模块
val javaCompatData: LiveData<String> = flow.asLiveData()
迁移前 ViewModel:
class OldViewModel : ViewModel() {
private val _data = MutableLiveData("")
val data: LiveData<String> = _data
private val _event = SingleLiveEvent<Event>()
val event: LiveData<Event> = _event
fun loadData() {
viewModelScope.launch {
_data.value = repository.fetchData()
_event.value = Event.ShowToast("Loaded")
}
}
}
迁移后 ViewModel:
class NewViewModel : ViewModel() {
private val _data = MutableStateFlow("")
val data: StateFlow<String> = _data.asStateFlow()
private val _event = MutableSharedFlow<Event>()
val event: SharedFlow<Event> = _event.asSharedFlow()
fun loadData() {
viewModelScope.launch {
_data.value = repository.fetchData()
_event.emit(Event.ShowToast("Loaded"))
}
}
}
通过本教程,可以系统性地将现有 LiveData 代码迁移到 Kotlin Flow。建议采用渐进式迁移策略,优先在新功能中使用 Flow,逐步改造旧代码。
.asStateFlow()
?private val _userName = MutableStateFlow("")
val userName: StateFlow<String> = _userName.asStateFlow()
这是为了 封装数据,隐藏可变性,提供只读视图。
.asStateFlow()
的作用:MutableStateFlow
(可变的)转换为 StateFlow
(只读的)。_userName
的值,只能读取。// 内部可以这样修改
_userName.value = "new name"
// 外部只能这样读
val current = userName.value
// ❌ 外部无法这样写
userName.value = "hack" // 编译报错
类似于 LiveData 的做法:
private val _name = MutableLiveData<String>()
val name: LiveData<String> = _name // 自动只读,无需转换
LiveData 本身用 val name: LiveData = _name
就能隐藏可变性。但 StateFlow
是一个 interface,MutableStateFlow
是它的子类,不写 .asStateFlow()
的话,别人可以看到它是可变的。
MutableStateFlow
和 StateFlow
有什么区别?特性 | MutableStateFlow |
StateFlow |
---|---|---|
是否可变 | ✅ 可读写 .value |
❌ 只读 .value |
使用场景 | 仅限在内部(ViewModel)中更新数据 | 向外部公开状态(UI层) |
类似类比 | MutableLiveData |
LiveData |
修改数据 | .value = newValue |
❌ 不可修改,只能读取 |
定义方式 | private val _x = MutableStateFlow(...) |
val x = _x.asStateFlow() |
MutableStateFlow
?// 不推荐这样
val userName = MutableStateFlow("") // 外部可改,破坏封装
这种写法让任何调用方都能随意修改值,违反了 Kotlin 和架构设计的封装原则。
.asStateFlow()
是 封装可变数据的一种手段,相当于 LiveData 中的 val name: LiveData = _name
。MutableStateFlow
是内部使用的可变版本,StateFlow
是外部暴露的只读版本。