class MvvmViewModel : ViewModel() {
val countState = MutableLiveData(1)
fun add(num: Int) {
countState.postValue(countState.value as Int + num)
}
fun reduce(num: Int) {
countState.postValue(countState.value as Int - num)
}
}
接下来我们就需要实现Screen,在Screen中将Content和ViewModel结合起来
@Composable
fun Screen1(
navController: NavController
) {
val viewModel: MvvmViewModel = viewModel()
val count by viewModel.countState.observeAsState(0)
Content1(count = count,
{ navController.navigate(“screen2”) }
) {
viewModel.add(1)
}
}
我们可以看到借助于viewModel()方法我们可以在jetpack compose中很方便快捷的创建viewModel,同时也可以将livedata方便的转换为compose state,当state发生变化时,界面就会自动重组并显示,比原有安卓view体系使用起来方便很多。
MVI
MVI架构大多数人可能不是很了解,不过其实也不是很难,它把mvvm的双向绑定变成单向数据流,强调数据的单向流动和数据源唯一性,state不可变,view通过state渲染数据,viewmodel通过action改变state 在这里插入图片描述
Content代码和MVVM一样
ViewModel代码如下
class MVIViewModel : ViewModel() {
val viewState = MutableLiveData(ViewState())
val userIntent = Channel(Channel.UNLIMITED)
init {
handleAction()
}
private fun add(num: Int) {
viewState.value?.let {
viewState.postValue(it.copy(count = it.count + 1))
}
}
private fun reduce(num: Int) {
viewState.value?.let {
viewState.postValue(it.copy(count = it.count - 1))
}
}
private fun handleAction()
{
viewModelScope.launch {
userIntent.consumeAsFlow().collect {
when (it) {
is UiAction.AddAction -> add(it.num)
is UiAction.ReduceAction -> reduce(it.num)
}
}
}
}
data class ViewState(val count: Int = 1)
sealed class UiAction {
class AddAction(val num: Int) : UiAction()
class ReduceAction(val num: Int) : UiAction()
}
}
Screen代码如下
@Composable
fun Screen1(
navController: NavController
) {
val viewModel: MVIViewModel = viewModel(navController = navController)
val viewState by viewModel.viewState.observeAsState(MVIViewModel.ViewState())
val coroutine = rememberCoroutineScope()
Content1(count = viewState.count,
{ navController.navigate(“screen2”) }
) {
coroutine.launch {
viewModel.userIntent.send(MVIViewModel.UiAction.AddAction(1))
}
}
}
通过上诉代码我们应该可以体会出mvvm和mvi之间的区别
多page通信问题
对于一个应用来说,通常不可能只会有一个page,由于mvvm和mvi的viewmodel都是和page绑定的,对于多个page来说,要想实现跨page通信可能比较麻烦,这也是目前mvvm和mvi的一个大问题。不过这个问题也很好解决,我们可以定义一个方法可以获取其他page的viewmodel或者全局的viewmodel即可。不过在compose中该如何实现,首先我们要了解compose中的viewmodel是如何保存的
通常compose都是和navigation来实现page跳转的,对于上面的viewModel()方法,我们分析源码可以发现,每跳转一个新的page,它都会新建一个新的ViewModelStoreOwner(即NavBackStackEntry),所以如果我们不指定ViewModelStoreOwner的话我们是获取不到上一个page和全局的viewmodel的,因此我们可以提供一个创建viewModel的方法,在创建时候先去获取当前路由栈和全局中存在的viewModel,获取不到的话再新建或者抛一个异常出去,这样就可以在page中获取到其他page的viewModel,实现page间的通信了 代码也非常简单,如下
@Suppress(“MissingJvmstatic”)
@Composable
inline fun viewModelOfNav(
navController: NavController,
key: String? = null,
factory: ViewModelProvider.Factory? = null
): VM {
val javaClass = VM::class.java
var viewModelStoreOwner: ViewModelStoreOwner? = null
navController.backQueue.forEach {
if (it.existViewModel(javaClass, key = key)) {
viewModelStoreOwner = it
return@forEach
}
}
if (viewModelStoreOwner == null) {
val javaClass = VM::class.java
var viewModelStoreOwner: ViewModelStoreOwner? = null
navController.backQueue.forEach {
if (it.existViewModel(javaClass, key = key)) {
viewModelStoreOwner = it
return@forEach
}
}
if (viewModelStoreOwner == null) {