从 Activity 导航到 Fragment 并返回到 Activity 导致该 Activity 被多次创建

Posted

技术标签:

【中文标题】从 Activity 导航到 Fragment 并返回到 Activity 导致该 Activity 被多次创建【英文标题】:Navigation to Fragment from Activity and back to the Activity is causing the activity to be created multiple times 【发布时间】:2021-10-27 14:26:42 【问题描述】:

我遇到的问题似乎很奇怪。

言辞

我有一个底部导航菜单的应用程序,其中包含 3 个按钮、每个按钮的 3 个片段和一个 MainActivity。当导航到任何这些片段时,一切都按预期工作。 当我从这 3 个片段中的任何一个导航到另一个片段(我们称之为片段 4 或 fr4)时,就会出现问题。 假设我在 fr1,我有一个按钮可以将我带到 fr4。当我回到 fr1(使用 android 后退按钮或按下 fr1 的底部栏按钮)时,我在主要活动或 3 个片段中的任何一个中所做的每件事都会重复 2 次。如果我再去fr4,然后再回到fr1,那么一切都会重复3次,依此类推。

在下面的代码中,fr1 是 fragment_home,fr4 是 fragment_profile。

代码

MainActivity.kt

class MainActivity : AppCompatActivity() 
    private lateinit var navView: BottomNavigationView
    private lateinit var binding: ActivityMainBinding

    private val sharedViewModel: SharedViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        navView = binding.navView
        val navController = findNavController(R.id.nav_host_fragment_activity_main)
        navView.setupWithNavController(navController)
    
    override fun onStart() 
        super.onStart()
        Timber.i("onStart main activity")
    

    override fun onStop() 
        super.onStop()
        Timber.i("onStop main activity")
    


framgnet1.kt

class HomeFragment : Fragment() 
    private var _binding: FragmentHomeBinding? = null
    private val binding get() = _binding!!
    private val sharedViewModel: SharedViewModel by activityViewModels()
    private val homeViewModel: HomeViewModel by viewModels()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? 

        _binding = FragmentHomeBinding.inflate(inflater, container, false)
        val root: View = binding.root

        binding.lifecycleOwner = viewLifecycleOwner
        binding.sharedViewModel = sharedViewModel
        binding.homeViewModel = homeViewModel


        binding.historyButton.setOnClickListener
             Timber.i("profile button clicked")
        

        binding.profileButton.setOnClickListener  view ->
            profileButtonClicked(view)
        

        return root
    


    fun profileButtonClicked() 
        Timber.i("profile button clicked")
        val action = HomeFragmentDirections.homeToProfileAction()
        NavHostFragment.findNavController(this).navigate(action)
    



mobile_navigation.xml

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mobile_navigation"
    app:startDestination="@+id/navigation_home">

    <fragment
        android:id="@+id/navigation_home"
        android:name="com.comp.comp.ui.home.HomeFragment"
        android:label="@string/title_home"
        tools:layout="@layout/fragment_home">
        <action
            android:id="@+id/home_to_profile_action"
            app:destination="@id/fragment_profile"
            app:launchSingleTop="true" />
    </fragment>

    <fragment
        android:id="@+id/navigation_dashboard"
        android:name="com.comp.comp.ui.dashboard.DashboardFragment"
        android:label="@string/title_dashboard"
        tools:layout="@layout/fragment_dashboard" >
    </fragment>

    <fragment
        android:id="@+id/navigation_notifications"
        android:name="com.comp.comp.ui.notifications.NotificationsFragment"
        android:label="@string/title_notifications"
        tools:layout="@layout/fragment_notifications" />
    <fragment
        android:id="@+id/fragment_profile"
        android:name="com.comp.comp.fragment_profile"
        android:label="fragment_profile"
        tools:layout="@layout/fragment_profile" />

</navigation>

我尝试在导航操作上切换 launchSingleTop="true" 无济于事。

会发生以下情况:

    在主页片段中,如果我按下历史按钮,它会打印一次“点击个人资料按钮” 如果我随后点击个人资料按钮,应用程序将导航到个人资料片段 我可以使用返回按钮或按下底部栏上的主页按钮返回主页片段 如果我现在按下历史按钮,“点击的配置文件按钮”会打印两次。

如果我重复上述步骤,那么下次我按下历史按钮时,它将打印“点击个人资料按钮”3 次,依此类推。

我还测试过转到另一个活动,如果我以前去过个人资料页面一次,我的主要活动中的onStop() 方法也会运行两次。当我回到主要活动时,onStart() 方法运行两次。根据我进入个人资料页面的次数,我所做的一切都会运行 2 次(或更多)。每次我访问同时存在的个人资料页面时,它似乎都会创建一个主要活动。任何想法为什么?

【问题讨论】:

【参考方案1】:

我尝试了一切都无济于事。然后我意识到,在每次配置更改时,它都会种植一个新的 Jake Wharton 的 Timber 调试树,并且它会记录尽可能多的树的所有内容。 希望这可以帮助某人。我的错误是木材​​刨在 MainActivity 的 onCreate 中,而它应该在 Application 的 OnCreate 中。

【讨论】:

以上是关于从 Activity 导航到 Fragment 并返回到 Activity 导致该 Activity 被多次创建的主要内容,如果未能解决你的问题,请参考以下文章

使用 Activity 的工具栏从 Fragment 向上导航

(导航组件)返回首页fragment时如何在activity上显示返回箭头?

如何在 JetPack Navigation 组件中托管 Fragment 的 Activity 中调用导航 Fragments 函数?

Kotlin:是不是可以从 Activity-1 的 Fragment 移动到 Activity-2

从 Activity AlertDialog 添加到 Fragment ArrayAdapter

操作栏和导航抽屉 - 使用 Activity/Fragment 滑动操作栏