Android:使用 BottomNavigationView、NavController 和 SafeArgs 恢复片段

Posted

技术标签:

【中文标题】Android:使用 BottomNavigationView、NavController 和 SafeArgs 恢复片段【英文标题】:Android: Fragment restore with BottomNavigationView, NavController and SafeArgs 【发布时间】:2021-06-03 08:46:00 【问题描述】:

我目前正在开发一个 android 应用程序,遇到了有关 BottomNavigationView 和 Fragments 的问题。我知道,我也有类似的问题,但要么没有解决我的问题,要么没有有效的答案。

我的应用由五个***目的地片段组成。为了在它们之间导航,我使用了 BottomNavigationView。此外,我有几个片段用作较低级别的目的地,将从***片段之一调用。我使用 SafeArgs 插件导航到这些片段并将数据传递给。

我的 BottomNavigationView 配置如下所示:

val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = (supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment).navController

val appBarConfiguration = AppBarConfiguration(setOf(
    R.id.navigation_dest1, R.id.navigation_dest2, R.id.navigation_dest3,
    R.id.navigation_dest4, R.id.navigation_dest5))
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController);

这种用法的问题在于,BottomNavigationView 似乎没有提供对在某处保存和存储片段以及重用这些实例进行导航的支持。它只是创建一个新实例并显示它。

目前每个片段都包含一些数据获取代码,例如在协程中运行网络请求或从文件系统加载文件。而且由于 BottomNavigationView 不保留片段实例,因此这些数据获取部分运行得太频繁了。 当然,我考虑过将数据获取过程放到主 Activity 中,但这会导致整个应用程序启动速度变慢,并且不能解决每次用户在片段之间导航时仍需要重新创建片段的问题。

到目前为止,我已经找到了一半的解决方案。通过使用 SupportFragmentManager,手动添加、显示和隐藏我的片段可以正常工作。但是该应用程序运行速度明显变慢,并且使用 SafeArgs 导航到我的较低级别目的地不再起作用。我使用 SafeArgs 是因为它易于使用且非常轻松,而且我想继续使用它。

我尝试使用 SupportFragmentManager 手动管理这一切,但最终导致混乱且性能更差。

有什么方法可以解决我的问题吗?一种方式,BottomNavigationView 可以与 SafeArgs 和 SupportFragmentManager 交互以重用片段,而不是在每个导航操作上重新创建它们?

(如果您需要更多信息或我的部分代码,请询问。我认为在这里发布我的完整代码没有多大意义。)

【问题讨论】:

对于所有看到此线程的人:该解决方案不能解决我最初描述的确切问题,但它解决了导致我遇到导航问题的架构问题。 【参考方案1】:

您是否考虑过与片段共享 ViewModel 的选项?例如:

创建一个 ViewModel 类,如下所示:

 class MyViewModel: ViewModel() 
    ....
    ....
    

然后,由于您的片段共享相同的 Activity,您可以声明以下内容(在 Kotlin 中):

 class MyFragment1: Fragment() 
        val viewModel: MyViewModel by activityViewModels()
        ....
        ....  
    

    class MyFragment2: Fragment() 
        val viewModel: MyViewModel by activityViewModels()
        ....
        ....  

    

在这种情况下,Fragment1 和 Fragment2 将共享同一个 ViewModel 实例,并且 ViewModel 将保留在内存中,直到 Activity 被销毁。 导航出去时不会保留片段,但您可以保留每个片段的所有数据并重新使用它们。它既快速又流畅,您不会介意是否重新创建片段,因为它的所有数据都将保存在内存中并准备好在共享 ViewModel 中使用。

另见官方文档: ViewModel Overview

【讨论】:

是的,我也试过这个。我遇到的问题是我需要在启动时执行网络请求。因为 Android 不允许我在主线程中运行它们,我使用带有 IO 调度程序的协程。当我在数据获取完成后尝试更新我的 ViewModel 时,我收到一条错误消息:无法从后台线程设置 viewModel。而且数据获取不是唯一的问题,在我的片段中,我显示了几张 HighDPI 图片,因此重新创建视图也需要时间,我认为 ViewModel 无法解决 ViewModel 有自己的协程上下文。当您通过挂起函数在 ViewModel 中设置数据时,请确保使用 withContext(Dispatcher.Main).launch ... your initialization code here 它会起作用 我忘了告诉你...将图片保存在 ViewModel 中。相信我这是最好的选择之一,您可以将 ViewModel 范围与 Coroutines 一起使用:例如:viewModel.viewModelScope.launch(Dispatchers.Main) 并在其中设置所有视图,您将避免遇到 UI 线程异常 感谢您的帮助,实际上它像这样工作得很好。不知何故,我没有设法解决 UI 异常,并且过早地放弃了这个想法。

以上是关于Android:使用 BottomNavigationView、NavController 和 SafeArgs 恢复片段的主要内容,如果未能解决你的问题,请参考以下文章

记录一下《猜歌猜谜》应用主要使用的开源库

Android 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )

Android 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )

Android之SharedPreferences使用

想要使用cordova/android禁用android的HardBack按钮

如何在Mac中使用Android SDK