【问题标题】:Compose - NavHost recomposition multiple times撰写 - NavHost 重组多次
【发布时间】:2022-07-27 23:55:50
【问题描述】:

在从 Navhost 导航期间,我发现可组合屏幕正在多次重新组合。因此,我的 ViewModel 也多次调用 API 数据源。

@Composable
fun MainView() {
    val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))
    val scope = rememberCoroutineScope()
    val navController = rememberNavController()
    Scaffold(
        scaffoldState = scaffoldState,
        topBar = { TopBar(
            toolbarTitle = stringResource(id = R.string.app_name),
            scope = scope,
            scaffoldState = scaffoldState
        ) },
        drawerContent = {
           DrawerView(scope = scope, scaffoldState = scaffoldState, navController = navController)
        },
    ) {
        NavGraph(navController = navController)
    }
}

@Composable
fun NavGraph(navController: NavHostController) {
    NavHost(navController, startDestination = NavDrawerItem.Repositories.route) {
        composable(NavDrawerItem.Repositories.route) {
            RepoListView(getViewModel())
        }

        composable(NavDrawerItem.EmojiList.route) {
            EmojiListView(getViewModel())
        }
    }
} 

class RepoListViewModel(
    private val repositoriesUseCase: GetRepositoriesUseCase
): ViewModel() {
    
    init {
        getRepositories()
    }

@Composable
fun RepoListView(viewModel: RepoListViewModel) {
    AppTheme {
        RepoListContent(viewModel)
    }
}

有办法处理吗? 我的意思是,我知道 Android Compose 就是这样工作的。但是,如何在导航屏幕中处理 API 调用?

编辑

问题出在 Koin 本身。一个新版本已经到来,现在它可以正常工作了。

【问题讨论】:

  • 你的 DrawerView 是什么样子的
  • getViewModel 不应在每次重组时创建新实例。我唯一的猜测是您可以多次拨打navigate,这将为每次呼叫创建一条新路线-请查看this answer了解详细信息。如果这没有帮助,请将您的代码更新为minimal reproducible example,因为现在它无法运行。
  • 正如我所说,如果没有minimal reproducible example,很难为您提供帮助。首先显示您的 Koin 模块设置。问题出现在初始屏幕出现期间或您尝试导航到其他屏幕时?在第二种情况下,显示您用于执行导航的代码
  • getViewModel() 是做什么的?如果它除了调用viewModel() 方法之外还做任何事情,那么这就是你的问题。预计,只要您在目的地之间进行动画处理,它们就会在每一帧上重新组合。

标签: android android-jetpack-compose android-jetpack-navigation


【解决方案1】:

如果您担心重组是多次网络调用的原因,您始终可以确保在每个可组合生命周期中使用 LaunchedEffect 只调用一次 viewModel。

所以,一种方法是这样的:

LaunchedEffect(Unit) {
    viewModel.getRepositories()
}

只要您传递给 LaunchedEffect 的密钥没有改变,它就不会重新组合。因此,只要可组合对象存在,使用 Unit 只会运行一次。

您是否在每次导航到该屏幕时都重新创建您的 viewModel?如果是这样,可能值得考虑将您的网络调用放在 init 块之外的另一个地方,并使用上面的 LaunchedEffect 方法,或者这样做,这样您就不会在每次导航时重新创建您的 viewModel。无论哪种方式最适合您的用例。

【讨论】:

  • 嘿,LaunchedEffect(Unit) 不在这里工作。换屏时仍然多次调用API
【解决方案2】:

在 composable() lambda 之外获取您的 viewModel。

@Composable
fun NavGraph(navController: NavHostController) {
    val viewModel: RepoListViewModel = ViewModel()
    NavHost(navController, startDestination = NavDrawerItem.Repositories.route) {
        composable(NavDrawerItem.Repositories.route) {
            RepoListView(viewModel)
        }

        composable(NavDrawerItem.EmojiList.route) {
            EmojiListView(viewModel)
        }
    }
} 

+如果您的 getViewModel() 使用 ViewModelProvider 检索 ViewModel,则将 getViewModel() 替换为对 viewModel() 的调用

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-18
    • 2015-05-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多