如何绑定ViewModel生命周期来compose

问题描述 投票:0回答:3

我现在正在使用 Jetpack Compose。 我意识到我可以为每个可组合项拥有 ViewModel 并在可组合项中初始化视图模型,就像这样:

val myViewModel:MyViewModel = viewModel()

但是存在一个问题,即使未显示可组合项,这些视图模型也永远不会被销毁。

例如,我有一个主可组合屏幕,它根据用户交互加载一些其他屏幕,如下所示:

@Composable
fun MainAuthentication(viewModel: MainViewModel) {
    val state = viewModel.state.value
    val scope = rememberCoroutineScope()
    val scaffoldState = rememberScaffoldState()
    Scaffold(scaffoldState = scaffoldState)
    {

        //--------------------(login and sign up button)--------------------//
        Row(
            modifier = Modifier
                .padding(top = 50.dp)
                .fillMaxSize(),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            if (!state.signUpFormIsVisible && !state.loginFormIsVisible) {
                Button(
                    onClick = {
                        viewModel.onEvent(event = MainEvent.LoginButtonClick)
                    },
                    modifier = Modifier
                        .padding(10.dp)
                        .weight(0.5f)
                ) {
                    Text(text = stringResource(id = R.string.login))
                }

                Button(
                    onClick = { viewModel.onEvent(event = MainEvent.SignUpButtonClick) },
                    modifier = Modifier
                        .padding(10.dp)
                        .weight(0.5f)
                ) {
                    Text(text = stringResource(id = R.string.signup))
                }
            }

        }



        LoginForm(show = state.loginFormIsVisible) { msg ->
            scope.launch {
                scaffoldState.snackbarHostState.showSnackbar(
                    message = msg
                )
            }
        }
        SignUpForm(show = state.signUpFormIsVisible) { msg ->
            scope.launch {
                scaffoldState.snackbarHostState.showSnackbar(
                    message = msg
                )
            }
        }


    }
}

每个登录和注册屏幕都有其视图模型,并且使用方式如下:

@Composable
fun LoginForm(show: Boolean, snackBarMsg: (String) -> Unit) {

    val viewModel: LoginViewModel = viewModel()
    val state = viewModel.state.value
 ...

AnimatedVisibility(
        visible = show,
        enter = slideInVertically(),
        exit = slideOutVertically()
    ) {
    ...
    ...
    }
}

如果可组合项不可见,视图模型被破坏,如何将每个视图模型绑定到其可组合函数?

如果相应的可组合项不可见,销毁视图模型是一个好习惯吗?

kotlin viewmodel android-jetpack-compose android-lifecycle
3个回答
4
投票

在 Compose 中,您可以使用 navigation,它非常适合您的需求:每个路线都有自己的视图模型范围,一旦从导航返回堆栈中删除路线,该视图模型范围就会被销毁。

您可以使用

popBackStack
在导航到新屏幕之前从堆栈中删除当前屏幕,旧屏幕将随相应的视图模型一起被销毁。查看这个答案,了解如何删除多个项目。

Compose Navigation 基于常规 Android 导航,因此它的文档与大多数问题相关,以防 Compose Navigation 文档对您来说很简短。


0
投票

ViewModel 应该独立于 UI。如果 UI 由于配置更改而被破坏,视图模型应保持完整并在重构时保留 UI 的状态。将视图模型绑定到每个可组合项是没有意义的。通常,每个屏幕应该只有一个视图模型,并且该屏幕上的所有可组合项都应该使用该视图模型。但这并不是一个硬性规则。当然,有些可组合项可以而且应该有自己的视图模型。但它们需要在更高级别上进行管理,以便当它们出现的屏幕不再使用时它们会被销毁。当您使用“后退”按钮从当前屏幕导航到上一个屏幕时,您通常会希望销毁该屏幕的所有视图模型。

有更好的方法可以让 UI 层次结构中的可组合项访问视图模型并销毁它们。我开发了一种解决方案,可以管理屏幕的视图模型,并允许层次结构中任何位置的可组合项轻松访问它们。还有一个功能是,即使当您返回到上一个屏幕时屏幕被破坏,视图模型也可以保持活动状态。这样做的一个用例是,如果您正在开发一个托管视频会议的应用程序,并且您希望在返回时继续使用摄像头和音频而不终止会议。为此,即使屏幕本身已被破坏,视图模型也需要保持活动状态。 Jetpack Compose 有自己的解决方案,但我对它们一直不满意。本机方法的两个最大缺点是您无法在屏幕之间传递对象,并且需要编写代码来检测设备配置设置和更改以适应屏幕。我用我开发的框架解决了这些问题。检查一下:

https://github.com/JohannBlake/Jetmagic


0
投票

当您使用函数

viewModel<YourViewModel>()
创建ViewModel时,创建了与本地
ViewModelStoreOwner
范围关联的ViewModel。
ViewModelStoreOwner
包含存储 ViewModel 的类 -
ViewModelStore
,您可以在其中使用函数
clear()
删除存储在其中的所有模型。

将 ViewModel 生命周期绑定到可组合项的方法之一,您可以创建自己的

ViewModelStoreOwner
,将创建的 ViewModel 与其关联,并在关闭屏幕内的可组合函数
viewModelStoreOwner.viewModelStore.clear()
中调用
DisposableEffect()

类似:

1.创建您自己的简单

ViewModelStoreOwner
:

val viewModelStoreOwnerLoginForm = object : ViewModelStoreOwner {
    override val viewModelStore: ViewModelStore = ViewModelStore()
}

2.使用函数

viewModel()
创建ViewModel时,在参数
viewModelStoreOwnerLoginForm
中传入
viewModelStoreOwner

val viewModel: LoginViewModel = viewModel<LoginViewModel>(
    viewModelStoreOwner = viewModelStoreOwnerLoginForm,
    key = "LoginViewModel"
)

3.在可组合函数

viewModelStoreOwnerLoginForm
中传递
LoginForm()
参数并在可组合函数
viewModelStoreOwnerLoginForm.viewModelStore.clear()
中调用
DisposableEffect()
:

@Composable
fun LoginForm(
    show: Boolean, 
    snackBarMsg: (String) -> Unit
    viewModelStoreOwnerLoginForm: ViewModelStoreOwner
) {
    //Your code of screen

    DisposableEffect(key1 = Unit, effect = {
        onDispose {
            //Code inside will work as the last thing after leaving the screen
            viewModelStoreOwnerLoginForm.viewModelStore.clear()
        }
    })
}

当屏幕

LoginForm
关闭时,函数
DispossableEffect()
将被调用,viewModel将被清除并销毁。

© www.soinside.com 2019 - 2024. All rights reserved.