【发布时间】:2020-12-31 06:43:47
【问题描述】:
我有一个带有FragmentContainerView 和BottomNavigationView 设置的Activity 应用程序:
<?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()
}
}
这是我从SplashFragment 调用naviagateToHome 的方式:
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: View androidx.fragment.app.FragmentContainerView{e7a7304 V.E...... ......ID 0,0-1080,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,它没有提供任何明确的解决方案。
注意:
如果我通过调用setupBottomNavigationBar 在onCreate 中设置底部导航,我不会收到错误消息,一切顺利。
我已经卡了一个多星期了,我不知道如何解决它。
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: 致命 例外:主要 进程:com.abc.dev,PID:3434 java.lang.IllegalStateException: 查看 androidx.fragment.app.FragmentContainerView{1414b18 V.E...... ......ID 0,0-1080,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)
【问题讨论】:
-
如果您使用的是 NavHostFragment,为什么还要手动运行 FragmentTransactions?
-
@EpicPandaForce 我想将整个导航图切换到另一个,如何在没有手动事务的情况下实现?
-
Tbh 您应该有 1 个图表,其中包含您的其他带有
<include的 XML,并且只需使用操作在图表之间移动。
标签: android kotlin android-fragments android-navigationview