Android Navcontroller 硬件后退按钮崩溃

标签 android android-jetpack android-jetpack-navigation

我正在使用新的 Android Storyboard 来创建应用程序。流程需要如下所示:

SplashFragment -> Fragment1 -> Fragment2

以下是 Storyboard(navigation_main.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/launch_navigation_graph"
            app:startDestination="@id/splashFragment">

    <fragment android:id="@+id/splashFragment" android:name="com.myapp.android.SplashFragment"
              android:label="fragment_splash" tools:layout="@layout/fragment_splash">
        <action android:id="@+id/action_splashFragment_to_fragment1"
                app:destination="@id/fragment1"/>
    </fragment>
    <fragment android:id="@+id/fragment1"
              android:name="com.myapp.android.Fragment1"
              android:label="fragment1" tools:layout="@layout/fragment_register_msisdn">
        <action android:id="@+id/action_fragment1_to_fragment2"
                app:destination="@id/fragment2" app:popUpTo="@+id/fragment1"
                app:enterAnim="@anim/nav_default_pop_enter_anim" app:exitAnim="@anim/nav_default_pop_exit_anim"/>
    </fragment>
    <fragment android:id="@+id/fragment2"
              android:name="com.myapp.android.Fragment2"
              android:label="fragment_fragment2" tools:layout="@layout/fragment_fragment2"/>
</navigation>

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="MainActivity">

    <fragment
            android:id="@+id/mainNavigationHostFragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:navGraph="@navigation/navigation_main" />

</androidx.constraintlayout.widget.ConstraintLayout>

应用程序主题没有操作栏,因为我不希望显示操作栏:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

基本上,我需要从 Fragment1Fragment2 导航,然后按硬件后退按钮,返回到 Fragment1。要从 Fragment1 导航到 Fragment2,我在 Fragment1 中有以下代码:

findNavController().navigate(R.id.action_fragment1_to_fragment2)

SplashFragment 不应在堆栈中维护,因为在启动中首次显示后不需要它。这就是为什么我只在 Fragment1Fragment2 操作中使用 popTo 。但是在运行相同的命令之后,从 Fragment2 按下回车键,第一次什么都不做(不弹出),第二次,它崩溃并出现以下异常:

2019-04-25 16:52:43.841 28598-28598/com.selfcare.safaricom E/InputEventSender: Exception dispatching finished signal.
2019-04-25 16:52:43.842 28598-28598/com.selfcare.safaricom E/MessageQueue-JNI: Exception in MessageQueue callback: handleReceiveCallback
2019-04-25 16:52:43.846 28598-28598/com.selfcare.safaricom E/MessageQueue-JNI: java.lang.IllegalArgumentException: navigation destination com.selfcare.safaricom:id/action_splashFragment_to_registerMSISDNFragment is unknown to this NavController
        at androidx.navigation.NavController.navigate(NavController.java:803)
        at androidx.navigation.NavController.navigate(NavController.java:744)
        at androidx.navigation.NavController.navigate(NavController.java:730)
        at androidx.navigation.NavController.navigate(NavController.java:718)
        at com.myapp.android.SplashFragment.handleLaunchStatus(SplashFragment.kt:51)
        at com.myapp.android.SplashFragment.access$handleLaunchStatus(SplashFragment.kt:16)
        at com.myapp.android.SplashFragment$attachLaunchObserver$1.onChanged(SplashFragment.kt:44)
        at com.myapp.android.SplashFragment$attachLaunchObserver$1.onChanged(SplashFragment.kt:16)
        at androidx.lifecycle.LiveData.considerNotify(LiveData.java:113)
        at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:126)
        at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:424)
        at androidx.lifecycle.LiveData$LifecycleBoundObserver.onStateChanged(LiveData.java:376)
        at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:361)
        at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:188)
        at androidx.lifecycle.LiveData.observe(LiveData.java:185)
        at com.myapp.android.SplashFragment.attachLaunchObserver(SplashFragment.kt:43)
        at com.myapp.android.SplashFragment.onViewCreated(SplashFragment.kt:35)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:895)
        at androidx.fragment.app.FragmentManagerImpl.addAddedFragments(FragmentManagerImpl.java:2092)
        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1866)
        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1822)
        at androidx.fragment.app.FragmentManagerImpl.popBackStackImmediate(FragmentManagerImpl.java:298)
        at androidx.fragment.app.FragmentManagerImpl.popBackStackImmediate(FragmentManagerImpl.java:241)
        at androidx.fragment.app.FragmentManagerImpl.popBackStackImmediate(FragmentManagerImpl.java:288)
        at androidx.fragment.app.FragmentManagerImpl.popBackStackImmediate(FragmentManagerImpl.java:241)
        at androidx.fragment.app.FragmentActivity$1.handleOnBackPressed(FragmentActivity.java:144)
        at androidx.activity.OnBackPressedDispatcher.onBackPressed(OnBackPressedDispatcher.java:136)
        at androidx.activity.ComponentActivity.onBackPressed(ComponentActivity.java:283)
        at android.app.Activity.onKeyUp(Activity.java:3083)
        at android.view.KeyEvent.dispatch(KeyEvent.java:2716)
        at android.app.Activity.dispatchKeyEvent(Activity.java:3366)
        at androidx.core.app.ComponentActivity.superDispatchKeyEvent(ComponentActivity.java:80)
        at androidx.core.view.KeyEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:84)
        at androidx.core.app.ComponentActivity.dispatchKeyEvent(ComponentActivity.java:98)
        at androidx.appcompat.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java:558)
        at androidx.appcompat.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:59)
        at androidx.appcompat.app.AppCompatDelegateImpl$AppCompatWindowCallback.dispatchKeyEvent(AppCompatDelegateImpl.java:2736)
        at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:342)
        at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:5037)
        at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4905)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4426)
        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4479)
        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4445)
        at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4585)
        at android.view.ViewRootImpl$InputStage.apply(Vie
2019-04-25 16:52:43.849 28598-28598/com.selfcare.safaricom E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.selfcare.safaricom, PID: 28598
    java.lang.IllegalArgumentException: navigation destination com.selfcare.safaricom:id/action_splashFragment_to_registerMSISDNFragment is unknown to this NavController
        at androidx.navigation.NavController.navigate(NavController.java:803)
        at androidx.navigation.NavController.navigate(NavController.java:744)
        at androidx.navigation.NavController.navigate(NavController.java:730)
        at androidx.navigation.NavController.navigate(NavController.java:718)
        at com.myapp.android.SplashFragment.handleLaunchStatus(SplashFragment.kt:51)
        at com.myapp.android.SplashFragment.access$handleLaunchStatus(SplashFragment.kt:16)
        at com.myapp.android.SplashFragment$attachLaunchObserver$1.onChanged(SplashFragment.kt:44)
        at com.myapp.android.SplashFragment$attachLaunchObserver$1.onChanged(SplashFragment.kt:16)
        at androidx.lifecycle.LiveData.considerNotify(LiveData.java:113)
        at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:126)
        at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:424)
        at androidx.lifecycle.LiveData$LifecycleBoundObserver.onStateChanged(LiveData.java:376)
        at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:361)
        at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:188)
        at androidx.lifecycle.LiveData.observe(LiveData.java:185)
        at com.myapp.android.SplashFragment.attachLaunchObserver(SplashFragment.kt:43)
        at com.myapp.android.SplashFragment.onViewCreated(SplashFragment.kt:35)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:895)
        at androidx.fragment.app.FragmentManagerImpl.addAddedFragments(FragmentManagerImpl.java:2092)
        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1866)
        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1822)
        at androidx.fragment.app.FragmentManagerImpl.popBackStackImmediate(FragmentManagerImpl.java:298)
        at androidx.fragment.app.FragmentManagerImpl.popBackStackImmediate(FragmentManagerImpl.java:241)
        at androidx.fragment.app.FragmentManagerImpl.popBackStackImmediate(FragmentManagerImpl.java:288)
        at androidx.fragment.app.FragmentManagerImpl.popBackStackImmediate(FragmentManagerImpl.java:241)
        at androidx.fragment.app.FragmentActivity$1.handleOnBackPressed(FragmentActivity.java:144)
        at androidx.activity.OnBackPressedDispatcher.onBackPressed(OnBackPressedDispatcher.java:136)
        at androidx.activity.ComponentActivity.onBackPressed(ComponentActivity.java:283)
        at android.app.Activity.onKeyUp(Activity.java:3083)
        at android.view.KeyEvent.dispatch(KeyEvent.java:2716)
        at android.app.Activity.dispatchKeyEvent(Activity.java:3366)
        at androidx.core.app.ComponentActivity.superDispatchKeyEvent(ComponentActivity.java:80)
        at androidx.core.view.KeyEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:84)
        at androidx.core.app.ComponentActivity.dispatchKeyEvent(ComponentActivity.java:98)
        at androidx.appcompat.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java:558)
        at androidx.appcompat.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:59)
        at androidx.appcompat.app.AppCompatDelegateImpl$AppCompatWindowCallback.dispatchKeyEvent(AppCompatDelegateImpl.java:2736)
        at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:342)
        at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:5037)
        at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4905)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4426)
        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4479)
        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4445)
2019-04-25 16:52:43.851 28598-28598/com.selfcare.safaricom E/AndroidRuntime:     at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4585)
        at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4453)
        at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4642)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4426)
        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4479)
        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4445)
        at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4453)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4426)
        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4479)
        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4445)
        at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4618)
        at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:4779)
        at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:2571)
        at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:2081)
        at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:2072)
        at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:2548)
        at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:141)
        at android.os.MessageQueue.nativePollOnce(Native Method)
        at android.os.MessageQueue.next(MessageQueue.java:326)
        at android.os.Looper.loop(Looper.java:160)
        at android.app.ActivityThread.main(ActivityThread.java:6718)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

SplashFragment 第 51 行是:

findNavController().navigate(R.id.action_splashFragment_to_fragment1)

如果我将 popTo 从 Fragment1 删除到 Fragment2 操作,则此异常消失,但后退按钮也不起作用。我在这里做错了什么?

编辑1:

根据 Stavro Xhardha 的评论,我对导航 XML 进行了一些修改:

<fragment android:id="@+id/splashFragment" android:name="com.myapp.android.SplashFragment"
          android:label="fragment_splash" tools:layout="@layout/fragment_splash">
    <action android:id="@+id/action_splashFragment_to_fragment1"
            app:destination="@id/fragment1"
    app:popUpToInclusive="true" app:popUpTo="@+id/splashFragment"/> <!--Added this line -->
</fragment>
<fragment android:id="@+id/fragment1"
          android:name="com.myapp.android.Fragment1"
          android:label="fragment1" tools:layout="@layout/fragment_register_msisdn">
    <action android:id="@+id/action_fragment1_to_fragment2"
            app:destination="@id/fragment2" app:popUpTo="@+id/fragment1"/>
</fragment>
<fragment android:id="@+id/fragment2"
          android:name="com.myapp.android.Fragment2"
          android:label="fragment_fragment2" tools:layout="@layout/fragment_fragment2"/>

并在MainActivity中重写onBackPressed,如下所示:

override fun onBackPressed() {
    super.onBackPressed()
    if (!findNavController(R.id.launchNavigationHostFragment).navigateUp()) {
        finish()
    }
}

现在 Fragment2 弹出到 Fragment1,但随后按 Fragment1 继续使 Fragment1 进入循环。我无法退出该应用程序。

最佳答案

我终于找到了问题并解决了它。

问题是,我正在从 ViewModel 观察 MutableLiveData 并根据其值进行导航。但我不知道fragment的生命周期所有者倾向于销毁Observer并根据 View 生命周期重新实例化它以避免泄漏。因此,一旦发生导航,观察者就不再存在,并且导航的代码位于观察者内。返回时需要相同的代码,因此当尝试访问它时,代码会崩溃。

我通过使用一个接口(interface)在需要完成导航时向 fragment 提供回调来解决这个问题。

关于Android Navcontroller 硬件后退按钮崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55848234/

相关文章:

java - 如何在android中发出具有多个属性的ksoap2请求?

android - 如何为 AndroidViewModel 创建 View 模型工厂?

android - PagedListAdapter 重新加载没有占位符的数据

android - Jetpack Compose Navigation - 将参数传递给 startDestination

android - 我可以使用字符串资源在 Jetpack Navigation XML 文件中生成深层链接吗?

android - 抽屉导航 xml 错误

Android - Recycler View onClick 监听器不工作

android - appcompat_v7 : Error retrieving parent for item: No resource found that matches the given name

android - 如何使用 View 绑定(bind)制作 BaseFragment

android - NavHostFragment 与 FragmentContainerView [Android]