-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
如何清空backstack再启动一个新fragment呢 #47
Comments
在使用官方demo的时候,我把 LaunchModeFragment 中 binding.btnPoptohome.setOnClickListener { 再点击HomeFragment 中的任何按钮都崩溃, |
目前做不到,NavController相关的方法基本都没法对底栈做处理,也让替换第一个fragment的行为变得束手无策; |
从popTo入手,观察了一下popBackStackInternal的实现,发现他是搞了一个for循环popStack,我在fragivity官方demo里面的LaunchModeFragment 中binding.btnPoptohome.setOnClickListener 中实验了如下反射代码,实验中我多点了几个标准的LaunchModeFragment 放在popStack中,目测是可以清掉后台backstack中所有fragment,除了root的HomeFragment,不过我自己都觉得太野蛮了些,一路反射,最后也需要loadroot才能加载想要的页面。 还是有些问题,等明天再研究一下loadroot的实现 val navController = navigator.navController
val declaredField =
androidx.navigation.NavController::class.java.getDeclaredField("mBackStack")
val declaredField2 =
androidx.navigation.NavController::class.java.getDeclaredField("mNavigatorProvider")
declaredField2.isAccessible = true
declaredField.isAccessible = true
val mNavigatorProvider = declaredField2.get(navController) as NavigatorProvider
val mBackStack = declaredField.get(navController) as Deque<NavBackStackEntry>
if (mBackStack.isEmpty()) {
// Nothing to pop if the back stack is empty
// return
}
val popOperations = mutableListOf<Navigator<*>>()
val iterator: Iterator<NavBackStackEntry> = mBackStack.descendingIterator()
var foundDestination = false
while (iterator.hasNext()) {
val destination = iterator.next().destination
val navigator: Navigator<*> = mNavigatorProvider.getNavigator(
destination.navigatorName
)
popOperations.add(navigator)
}
var popped = false
for (item in popOperations) {
popped = if (item.popBackStack()) {
val entry: NavBackStackEntry = mBackStack.removeLast()
if (entry.lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
val declaredMethod =
androidx.navigation.NavBackStackEntry::class.java.getDeclaredMethod(
"setMaxLifecycle",
Lifecycle.State::class.java
)
declaredMethod.isAccessible = true
declaredMethod.invoke(entry, Lifecycle.State.DESTROYED)
}
true
} else {
// The pop did not complete successfully, so stop immediately
break
}
}
val declaredMethod =
androidx.navigation.NavController::class.java.getDeclaredMethod(
"updateOnBackPressedCallbackEnabled")
declaredMethod.isAccessible = true
declaredMethod.invoke(navController)
val activity = requireActivity() as MainActivity
val navHostFragment = activity.findOrCreateNavHostFragment(R.id.nav_host)
navHostFragment.loadRoot(HomeFragment::class) |
说到重复 |
刚刚在develop分支里新开发了一个pushTo方法,可以试下效果。 |
完美,navigation 终于可以用了,看了一下改动的源码,很巧妙啊,kotlin的扩展方法原来这么牛掰,就是为写扩展库用的啊。我还傻乎乎的反射。 |
Utils 中如果this.java.name.hascode容易冲突的话,可以考虑this.java.canonicalName 这个 带完整包名的名字,应该不会有冲突 |
navigator.showDialog打开DialogFragment后, 关闭 是navigator.navigateUp,还是 navigator.pop(), 用navigator 这种方式打开后,以前用dialogfragment 实例实现的监听好像用不了吧。以前都是DialogFragment()一个实例instance,然后instance.setCustomeListener定义一些监听进行相关处理。navigator有相关的支持吗 |
dialong方面库里就实现了自动添加了node,其他都是原有navigation那一套,关闭应该就是NavController.popBackStack; 我看了下这篇文章关于java:Name和CanonicalName有什么区别?,感觉输出差不多,而canonicalName在某些情况下会返回null,所以我暂时还是先用name。 |
发现一个bug , A->B->C. C pushTo 到A, 再返回 ,B 、 C都还在啊, 虽然从stack 弹窗显示,都不在,但点击返回键确能退回去,是不是graph里面的逻辑没有清掉 |
抱歉才恢复,我查看下问题
|
我参考了一下androidx.navigation.NavController#popBackStackInternal 中的方法,navigation自身的popTo 用的应该就是这个方法,他还是进行了一些细节处理, 在修改了一下Ext.kt 中的 internal fun NavController.clearBackStackEntry() 的方法如下,就可以了,但还是用到了反射。而且在多组件的情况下有时候会导致 一个问题,A->B->C ApushTo 到C ,会导致B的残像一直在,A要设置背景才能覆盖。
|
应该不用,我大致看了下原因,主要是没处理FragmentManager的返回栈,pushTo以后navController返回栈虽然空了不会消费返回事件,但是传到了FragmentManager里因为FragmentManager里的返回栈没清空触发了FragmentManager.popBackxxxx。 |
这种修复 会触发这种异常moveToState 触发 FragmentManagerViewModel.setIsStateSaved 的时候引发空指针异常 @qdsfdhvh |
@qdsfdhvh 这个补丁我应用后会crash,但demo 不会,那样更改的原理是啥? |
应该是有问题的,demo不会也许是场景比较简单;我也暂时没搞清楚哪个因素导致没执行onDestroy,我在fix里只是手动 |
@showwiki 试试看这个commit有没有修复问题,我初步查下来好像是 |
@qdsfdhvh 在bugly 上 有观察到 pushTo 会出现这种问题 , 不过是偶现的,我再观察一下吧 main(1)java.lang.IllegalArgumentException No destination with ID 0 is on the NavController's back stack. The current destination is null 解析原始 |
@qdsfdhvh @vitaviva 不知道是不是pushTo 导致的问题,现在push 操作和 pushTo 操作总会偶现 上面这个错误,查了一下发现是 CreateNode.kt中的62行,val nodeSaver = nodeSaver 的时候 调用 get() = ViewModelProvider(getViewModelStoreOwner(graph.id)) val graph = graph
val nodeSaver = nodeSaver |
@qdsfdhvh @vitaviva 最后发现是在activity 中 解决一个问题时,更改了activity的onSaveInstanceState的逻辑, 通过在 在onRestoreInstanceState 中增加 navHostFragment.navController.restoreState(savedInstanceState.getBundle("nav_state")) 解决了 |
遇到个问题,是如果我的任务栈已经叠加了两个fragment , 比如 A打开B, 此时回退可以看到A, 但如果此时想从B到C,那怎么清除在后台的A呢? 从B到C, popSelf 不能清除掉最下层的A,popTo C也不能清除A, 从B到C, pop 和push 连着调用 会导致fragment重影, 新开的C和A重叠在一起了。有什么办法可以清空任务栈后再启动一个新的fragment, 或者清空某个后退站底层的fragment后再压入一个。
The text was updated successfully, but these errors were encountered: