NavHostFragment在onDestroyView中崩溃

如何解决NavHostFragment在onDestroyView中崩溃

我有一个Activity应用,其中有一个FragmentContainerView一个BottomNavigationView设置:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <LinearLayout
        android:id="@+id/layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            app:defaultNavHost="true"
            app:navGraph="@navigation/splash" />

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottom_navigation"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:visibility="gone"
            app:elevation="12dp"
            app:labelVisibilityMode="unlabeled"
            app:menu="@menu/menu_bottom_nav_main" />
    </LinearLayout>
</layout>

用户进入应用程序时,我需要显示一个初始页面(如果需要,可以启动),然后将其导航到主页。因此,我创建了4个导航图,其中1个用于启动画面,3个用于底部导航。我正在使用Android示例中的this灭绝技术,通过3个图表来设置底部导航。这是我的MainActivity代码

@AndroidEntryPoint
class MainActivity : BaseActivity<ActivityMainBinding,Mainviewmodel>(
    R.layout.activity_main,Mainviewmodel::class,) {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (!vm.splashPassed) {
            val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
            currentNavController = mutablelivedata(navHostFragment.navController)
        } else if (savedInstanceState == null && vm.splashPassed) {
            naviagatetoHome()
        }
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        if (vm.splashPassed) {
            naviagatetoHome()
        }
    }

    fun naviagatetoHome() {
        vm.splashPassed = true
        val fragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        supportFragmentManager.beginTransaction()
            .remove(fragment)
            .commitNow()

        setupBottomNavigationBar()
    }

    private lateinit var currentNavController: LiveData<NavController>

    private fun setupBottomNavigationBar() {
        binding.bottomNavigation.isVisible = true
        val navGraphIds = arraylistof(R.navigation.explore,R.navigation.plan,R.navigation.profile)

        // Setup the bottom navigation view with a list of navigation graphs
        currentNavController = binding.bottomNavigation.setupWithNavController(
            navGraphIds = navGraphIds,fragmentManager = supportFragmentManager,containerId = R.id.nav_host_fragment,intent = intent,)
    }

    override fun onBackpressed() {
        val navController = currentNavController.value ?: return super.onBackpressed()

        if (navController.graph.id == R.id.splash) return super.onBackpressed()

        val destination = navController.currentDestination?.id
        val rootDestinations = listof(R.id.planFragment,R.id.profileFragment,R.id.exploreFragment,R.id.panelFragment)
        if (destination !in rootDestinations && navController.navigateUp()) return

        super.onBackpressed()
    }
}

这就是我从naviagatetoHome呼叫SplashFragment的方式:

vm.navigateHome.observe(viewLifecycleOwner) {
    (requireActivity() as? MainActivity)?.naviagatetoHome()
}

一切正常,直到Activity执行onDestroy(被推到后台时)为止,并且崩溃并带有以下堆栈跟踪:

2020-09-13 13:17:38.064 28092-28092 / com.abc.dev E / AndroidRuntime: 致命异常:主要 流程:com.abc.dev,PID:28092 java.lang.RuntimeException:无法销毁活动{com.abc.dev/com.abc.ui.activity.MainActivity}: java.lang.IllegalStateException:视图 androidx.fragment.app.FragmentContainerView {e7a7304 V.E ...... ...... ID 0,0-1080,1868#7f0a026a app:id / nav_host_fragment}没有 导航控制器集 在android.app.ActivityThread.performDestroyActivity(ActivityThread.java:4941) 在android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:4970) 在android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:44) 在android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) 在android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) 在android.app.ActivityThread $ H.handleMessage(ActivityThread.java:2016) 在android.os.Handler.dispatchMessage(Handler.java:107) 在android.os.Looper.loop(Looper.java:214) 在android.app.ActivityThread.main(ActivityThread.java:7356) 在java.lang.reflect.Method.invoke(本机方法) 在com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run(RuntimeInit.java:492) 在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) 造成原因:java.lang.IllegalStateException:查看androidx.fragment.app.FragmentContainerView {e7a7304 V.E ...... ...... ID 0,1868#7f0a026a app:id / nav_host_fragment}没有 导航控制器集 在androidx.navigation.Navigation.findNavController(Navigation.java:84) 在androidx.navigation.fragment.NavHostFragment.onDestroyView(NavHostFragment.java:388) 在androidx.fragment.app.Fragment.performDestroyView(Fragment.java:3171) 在androidx.fragment.app.FragmentStateManager.destroyFragmentView(FragmentStateManager.java:726) 在androidx.fragment.app.FragmentStateManager.movetoExpectedState(FragmentStateManager.java:360) 在androidx.fragment.app.FragmentStore.movetoExpectedState(FragmentStore.java:112) 在androidx.fragment.app.FragmentManager.movetoState(FragmentManager.java:1632) 在androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3109) 在androidx.fragment.app.FragmentManager.dispatchDestroy(FragmentManager.java:3088) 在androidx.fragment.app.FragmentController.dispatchDestroy(FragmentController.java:334) 在androidx.fragment.app.FragmentActivity.onDestroy(FragmentActivity.java:322) 在androidx.appcompat.app.AppCompatActivity.onDestroy(AppCompatActivity.java:278) 在android.app.Activity.performDestroy(Activity.java:8048) 在android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1334) 在android.app.ActivityThread.performDestroyActivity(ActivityThread.java:4926) 在android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:4970) 在android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:44) 在android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) 在android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) 在android.app.ActivityThread $ H.handleMessage(ActivityThread.java:2016) 在android.os.Handler.dispatchMessage(Handler.java:107) 在android.os.Looper.loop(Looper.java:214) 在android.app.ActivityThread.main(ActivityThread.java:7356) 在java.lang.reflect.Method.invoke(本机方法) 在com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run(RuntimeInit.java:492) 在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

花了一些时间进行调试后,我意识到androidx.navigation.fragment.NavHostFragment.onDestroyView尝试使用Navigation.findNavController方法,该方法存在一些here描述的问题。我还在android / architecture-components-samples GitHub存储库中找到了一个pull request,它没有提供任何明确的解决方案。

注释: 如果我通过调用onCreatesetupBottomNavigationBar中设置了底部导航,则不会收到错误消息,一切都会顺利进行。

我坚持了一个多星期,不知道如何解决

tnx,阅读了我的长问。

更新1: 我已经注意到,如果我至少一次更改了所选的底部导航项,则不会发生崩溃。

如果我想在分离前一个NavHostFragment添加自定义动画,如下所示,则始终出现相同的错误

fun naviagatetoHome() {
    vm.splashPassed = true
    val fragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    supportFragmentManager.beginTransaction()
        .setCustomAnimations(R.anim.nav_default_enter_anim,R.anim.nav_default_exit_anim)
        .remove(fragment)
        .commitNow()

    setupBottomNavigationBar()
}

2020-09-14 00:18:37.705 3434-3434 / com.abc.dev E / AndroidRuntime:FATAL 例外:主要 流程:com.abc.dev,PID:3434 java.lang.IllegalStateException:查看androidx.fragment.app.FragmentContainerView {1414b18 V.E ...... ...... ID 0,1868#7f0a026a app:id / nav_host_fragment}没有 导航控制器集 在androidx.navigation.Navigation.findNavController(Navigation.java:84) 在androidx.navigation.fragment.NavHostFragment.onDestroyView(NavHostFragment.java:388) 在androidx.fragment.app.Fragment.performDestroyView(Fragment.java:3171) 在androidx.fragment.app.FragmentStateManager.destroyFragmentView(FragmentStateManager.java:726) 在androidx.fragment.app.FragmentStateManager.movetoExpectedState(FragmentStateManager.java:360) 在androidx.fragment.app.SpecialEffectsController $ FragmentStateManagerOperation.complete(SpecialEffectsController.java:512) 在androidx.fragment.app.DefaultSpecialEffectsController.removeCancellationSignal(DefaultSpecialEffectsController.java:81) 在androidx.fragment.app.DefaultSpecialEffectsController $ 4 $ 1.run(DefaultSpecialEffectsController.java:255) 在android.os.Handler.handleCallback(Handler.java:883) 在android.os.Handler.dispatchMessage(Handler.java:100) 在android.os.Looper.loop(Looper.java:214) 在android.app.ActivityThread.main(ActivityThread.java:7356) 在java.lang.reflect.Method.invoke(本机方法) 在com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run(RuntimeInit.java:492) 在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

解决方法

这是导航示例中代码的一部分,该代码确定要附加和分离其他片段的片段。

    // Attach or detach nav host fragment depending on whether it's the selected item.
    if (this.selectedItemId == graphId) {
        selectedNavController.value = navHostFragment.navController
            attachNavHostFragment(fragmentManager,navHostFragment,index == 0)
    } else {
        detachNavHostFragment(fragmentManager,navHostFragment)
    }

这是怎么回事?
attach :将NavHostFragment与容器附加在一起时, onViewCreated会被调用,它还会为父视图(即FragmentContainerView)设置NavController。

分离:调用NavHostFragment从容器onDestroyView分离时。 onDestroyView将父视图的NavController设置为null。

attach仅针对selectedFragmentId被调用,所有其他片段都被分离。

以下是您的日志记录,将第一个片段视为选定片段:

2020-09-14 09:34:58.602 12023-12023/com.beetlestance.testingnavigation D/navHostLifecycle: onDestroyView
2020-09-14 09:34:58.602 12023-12023/com.beetlestance.testingnavigation D/navController: SplashFragment removing navController from parent
2020-09-14 09:34:58.611 12023-12023/com.beetlestance.testingnavigation D/bottomSheet: obtain navHostFragment 1
2020-09-14 09:34:58.616 12023-12023/com.beetlestance.testingnavigation D/navHostLifecycle: onCreate
2020-09-14 09:34:58.620 12023-12023/com.beetlestance.testingnavigation D/navHostLifecycle: onViewCreated
2020-09-14 09:34:58.620 12023-12023/com.beetlestance.testingnavigation D/navController: FirstBottomFragment setting navController to parent
2020-09-14 09:34:58.633 12023-12023/com.beetlestance.testingnavigation D/navHostLifecycle: onResume
2020-09-14 09:34:58.634 12023-12023/com.beetlestance.testingnavigation D/bottomsheet: attach 1
2020-09-14 09:34:58.635 12023-12023/com.beetlestance.testingnavigation D/bottomSheet: obtain navHostFragment 2
2020-09-14 09:34:58.640 12023-12023/com.beetlestance.testingnavigation D/navHostLifecycle: onCreate
2020-09-14 09:34:58.643 12023-12023/com.beetlestance.testingnavigation D/navHostLifecycle: onViewCreated
2020-09-14 09:34:58.643 12023-12023/com.beetlestance.testingnavigation D/navController: SecondBottomFragment setting navController to parent
2020-09-14 09:34:58.654 12023-12023/com.beetlestance.testingnavigation D/navHostLifecycle: onResume
2020-09-14 09:34:58.656 12023-12023/com.beetlestance.testingnavigation D/bottomsheet: detach 2
2020-09-14 09:34:58.660 12023-12023/com.beetlestance.testingnavigation D/navHostLifecycle: onDestroyView
2020-09-14 09:34:58.660 12023-12023/com.beetlestance.testingnavigation D/navController: SecondBottomFragment removing navController from parent
2020-09-14 09:34:58.661 12023-12023/com.beetlestance.testingnavigation D/bottomSheet: obtain navHostFragment 3
2020-09-14 09:34:58.666 12023-12023/com.beetlestance.testingnavigation D/navHostLifecycle: onCreate
2020-09-14 09:34:58.668 12023-12023/com.beetlestance.testingnavigation D/navHostLifecycle: onViewCreated
2020-09-14 09:34:58.668 12023-12023/com.beetlestance.testingnavigation D/navController: ThirdBottomFragment setting navController to parent
2020-09-14 09:34:58.678 12023-12023/com.beetlestance.testingnavigation D/navHostLifecycle: onResume
2020-09-14 09:34:58.680 12023-12023/com.beetlestance.testingnavigation D/bottomsheet: detach 3
2020-09-14 09:34:58.684 12023-12023/com.beetlestance.testingnavigation D/navHostLifecycle: onDestroyView
2020-09-14 09:34:58.684 12023-12023/com.beetlestance.testingnavigation D/navController: ThirdBottomFragment removing navController from parent

从日志中很明显,分离是最后一个调用而不是附加,这将导致父级NavController为空。

解决方案: 最后应附加选定的片段,这将在此处解决问题。如果默认情况下您为底部导航选择了不同的ID,则可以仅将片段附加到forEach循环之外,而不必反转列表。

另一种方法是将您的底页贴在片段内,这将简化大多数情况。飞溅片段将始终是导航到您的底部导航容器片段的起始目的地,该片段将在onViewCreated中设置底部工作表。选中https://github.com/beetlestance/android-extensions

onCreate中进行设置为什么会起作用?

仅在至少开始活动时才附加或分离片段。因此,当您在onCreate中设置底部导航时,NavHostFragment的生命周期会有所不同。

onCreate中设置了底部导航的情况下的日志:

2020-09-14 10:07:17.659 15260-15260/com.beetlestance.testingnavigation D/bottomSheet: obtain navHostFragment 1
2020-09-14 10:07:18.011 15260-15260/com.beetlestance.testingnavigation D/navHostLifecycle: onCreate
2020-09-14 10:07:18.017 15260-15260/com.beetlestance.testingnavigation D/bottomsheet: attach 1
2020-09-14 10:07:18.041 15260-15260/com.beetlestance.testingnavigation D/bottomSheet: obtain navHostFragment 2
2020-09-14 10:07:18.069 15260-15260/com.beetlestance.testingnavigation D/navHostLifecycle: onCreate
2020-09-14 10:07:18.073 15260-15260/com.beetlestance.testingnavigation D/bottomsheet: detach 2
2020-09-14 10:07:18.077 15260-15260/com.beetlestance.testingnavigation D/bottomSheet: obtain navHostFragment 3
2020-09-14 10:07:18.098 15260-15260/com.beetlestance.testingnavigation D/navHostLifecycle: onCreate
2020-09-14 10:07:18.108 15260-15260/com.beetlestance.testingnavigation D/bottomsheet: detach 3
2020-09-14 10:07:18.173 15260-15260/com.beetlestance.testingnavigation D/mainActivityLifecycle: onStart
2020-09-14 10:07:18.208 15260-15260/com.beetlestance.testingnavigation D/navHostLifecycle: onViewCreated
2020-09-14 10:07:18.208 15260-15260/com.beetlestance.testingnavigation D/navController: FirstBottomFragment setting navController to parent
2020-09-14 10:07:18.292 15260-15260/com.beetlestance.testingnavigation D/mainActivityLifecycle: onResume
2020-09-14 10:07:18.304 15260-15260/com.beetlestance.testingnavigation D/navHostLifecycle: onResume

您可以检查onDestroyView是否未调用NavHostFragment。因此NavController已正确设置。

希望这会有所帮助。

,

问题是在最后调用detach而不是attach时引起的,这会迫使navController在父级NavHostFragment上为null。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?