在Compose开发中,数据层推荐使用的数据类型是Flow和mutableState,对于简单类型的数据状态,可以直接在ViewModel中使用mutableStateof()或mutableStateListOf()声明。对于比较复杂的数据流处理,则建议通过flow实现,在Compose中调用flow.collectAsStateWithLifecycle()。
对于LiveData,虽然Compose也提供了liveData.observeAsState()的方法,但LiveData本身是有活跃状态周期的,在非活跃状态是接收不到数据变化的,因此在一些情况下会出现UI跟不上数据的问题,明明调用了liveData.postValue()方法,但是在Compose中却监听不到变化。
LiveData生命周期
LiveData的观察者是存在活跃状态跟非活跃状态的,在活跃状态中才能接受到数据的更新。如果处于非活跃状态中,则需要等待状态恢复为活跃,此时依然可以收到最新数据,因为LiveData具有数据倒灌特性。
而LiveData的活跃状态定义为LifeCycle.State.STARTED和LifeCycle.State.RESUMED,对于Activity的生命周期为OnStarted到OnPause之间
Compose的observeAsState分析
在Compsoe中,可以通过LiveData.observeAsState将LiveData转化为mutableState,并且绑定到当前页面的lifecycle。但是,这是怎么实现的呢
查看源码,实现其实很简单
@Composable
fun <T> LiveData<T>.observeAsState(): State<T?> = observeAsState(value)
@Composable
fun <R, T : R> LiveData<T>.observeAsState(initial: R): State<R> {
val lifecycleOwner = LocalLifecycleOwner.current
val state = remember {
@Suppress("UNCHECKED_CAST") /* Initialized values of a LiveData<T> must be a T */
mutableStateOf(if (isInitialized) value as T else initial)
}
DisposableEffect(this, lifecycleOwner) {
val observer = Observer<T> { state.value = it }
observe(lifecycleOwner, observer)
onDispose { removeObserver(observer) }
}
return state
}
observeAsState内部维护了一个mutableState类型的变量,通过DisposableEffect实现了对LiveData的注册和移除,使用了一个新的Observer对象,把liveData的值实时同步到mutableState中,而State就是Compose组件可以感应的数据。
如果你不知道DisposableEffect的作用,可以查看深入理解Jetpack Compose中的函数的执行顺序这篇文章,它的作用类似于页面的onCreate和onDestroy周期
问题分析
在Compose Screen中,如果是使用Navigation来作为页面的切换,则在navController.navigate方法后,前Screen会走onDispose的回调。
但如果是另起了一个新的Activity,那么前Screen并不会走onDispose回调。在这种情况下,如果使用了
val value: String by liveData. observeAsState("initial")
Text("Value is $value")
的方式来监听liveData,并且在新的Activity中,对liveData进行数据更新
liveData.postValue("new")
那么当返回前一个页面的时候,发现value的值并没有同步更新到
解决方案
分析observeAsState的源码和liveData机制,当页面恢复到活跃状态时,liveData会将最新值倒灌至观察者,那么数据的变化应该是可以监听到的。
但是Compose内部会对UI进行智能的更新,而observeAsState中的状态是保存在LiveData内部的,因此考虑到了状态提升
状态提升
Compose中建议将状态放在最上层的Compose组件中,基层的Compose组件都以无状态的形式,方便维护和管理
将liveData内的状态提升到Compose中如下:
@Composable
fun Test() {
val lifecycleOwner = LocalLifecycleOwner.current
var state by remember {
mutableStateOf("")
}
DisposableEffect(liveData, lifecycleOwner) {
val observer = Observer<String> {
state = it
}
liveData.observe(lifecycleOwner, observer)
onDispose { liveData.removeObserver(observer) }
}
Text(state)
}
测试后发现状态的更新可以被监测到了
通过ViewModel监听liveData
在viewModel中手动管理liveData的绑定和解除,由于viewModel中没有实现lifecycle,因此在init和onCleared()方法中处理
var state by mutableStateOf("")
private val observer = Observer<String> {
state = it
}
init {
livedata.observeForever(observer)
}
override fun onCleared() {
livedata.removeObserver(observer)
}
在Compose中,通过state获取数据最新值