本文介绍了在ViewModel内如何使用State/Flow/LivData触发重组更新UI,以及注意事项
1.前提:
重组由State#value触发,即State#value变化后,会触发重组。参考Compse之重组
2.在Compose的ViewModel中通过LiveData、Flow、State去操作数据,进而更新UI
- 对于 LiveData,需要将 LiveData 转换为 State
- 对于 Flow,需要将 Flow 转换为 State
3.小例子:
在可组合方法Greeting内,先展示原始数据,从TestRepo中获取数据后,赋值给s,触发重组,再次绘制Text
class TestViewModel : ViewModel() {
//Repository中间层 管理所有数据来源 包括本地的及网络的
private val mTestRepo = TestRepository()
private var _str1 = mutableStateOf("")
var str1: State<String> = _str1
private var _str2 = MutableStateFlow("")
var str2 = _str2.asStateFlow()
private val _str3 = MutableLiveData("")
val str3: LiveData<String> = _str3
fun getDateFromServer() {
launchRequest {
val result = mTestRepo.getDateFromServer()
if (result.items.isNotEmpty()) {
_str1.value = "1111"
_str2.value = "2222"
Log.d("lym", "getDateFromServer: ${_str1.value}, ${_str2.value}")
//MutableLiveData#setValue(T) 必须在主线程中调用
//MutableLiveData#postValue(T) 既可以在主线程中调用, 也可以在子线程中调用
//_str3.value = "3333" //error:Cannot invoke setValue on a background thread
_str3.postValue("3333")
}
}
}
}
class MainActivity : ComponentActivity() {
private val mViewModel: TestViewModel by lazy {
ViewModelProvider(this)[TestViewModel::class.java]
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Theme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background
) {
Log.d("lym", "onCreate: ")
Greeting(mViewModel)
}
}
}
}
}
@SuppressLint("StateFlowValueCalledInComposition")
@Composable
fun Greeting(vm: TestViewModel, modifier: Modifier = Modifier) {
// 将 Flow<T> 转换成 State<T>
val str2Value by vm.str2.collectAsStateWithLifecycle()
// 将 Flow<T> 转换成 State<T>
val str2ValueAnother by vm.str2.collectAsState()
// 将 LiveData<T> 转换成 State<T>
val str3Value by vm.str3.observeAsState()
LaunchedEffect(Unit) {
vm.getDateFromServer("Android", 0, 1)
}
// Log.d("lym", "Greeting: ${vm.str1.value}")
// val s = vm.str1.value
Log.d("lym", "Greeting: $str2Value")
val s = str2Value
// Log.d("lym", "Greeting: $str2ValueAnother")
// val s = str2ValueAnother
// Log.d("lym", "Greeting: $str3Value")
// val s = str3Value
val text = if (!TextUtils.isEmpty(s)) s else "Android"
Text(
text = "Hello ${text}!", modifier = modifier
)
}
3.1 Jetpace的组件ViewModel也是MVVM架构的ViewModel层,需要添加依赖
implementation "androidx.lifecycle:lifecycle-viewmodel:2.6.2"
3.2 Flow.collectAsState() & Flow.collectAsStateWithLifecycle()都可,如何选择
在 Android开发中使用collectAsStateWithLifecycle()方法来收集数据流,它会以生命周期感知型方式从 Flow 收集值。此时需要添加依赖
implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.1"
collectAsState() 不是生命周期感知的,通常用于跨平台的场景下(Compose也可以跨平台),且collectAsState不需引入其他库。
注意:Greeting方法内,无val str2ValueAnother by vm.str2.collectAsState()
,不会触发重组,即Text不会更新。collectAsState作用就是把Flow变为State, str2ValueAnother = State#value,State#value变化,触发重组。若只定义val str2ValueAnother by vm.str2.collectAsState()
,但不使用str2ValueAnother,也不会触发重组
3.3 LiveData.obseverAsState()
observeAsState()会开始观察此 LiveData,并在LiveData有数据更新时,自动将其转换为State ,进而触发可组合项的重组。此时需要添加依赖:
implementation "androidx.compose.runtime:runtime-livedata:1.1.1"
3.4 执行网络请求时,使用viewModelScope.launch来启动协程,引入方式:
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
4.参考
Jetpack Compose之在ViewModel中应该使用哪种State
Jetpack Compose | State状态管理及界面刷新 - 掘金 (juejin.cn)