我正在使用导航组件版本2.1.0-rc01
,我使用
Navigation.findNavController(it).navigate(R.id.action_participants)
第二次浏览相同的屏幕后,我可以看到第二个片段,但我收到异常。我在 FragmentManager 上启用了日志,似乎存在未附加的同一片段的不同实例,从而导致错误
关于为什么导航组件要创建未附加的片段的另一个实例的任何想法?有什么解决方法可以代替附加的片段吗?
2019-08-15 16:59:30.895 30041-30041/com.app.debug D/FragmentManager: mName=3-2131361912 mIndex=-1 mCommitted=false
2019-08-15 16:59:30.895 30041-30041/com.app.debug D/FragmentManager: mEnterAnim=#7f01001e mExitAnim=#7f01001f
2019-08-15 16:59:30.896 30041-30041/com.app.debug D/FragmentManager: mPopEnterAnim=#7f010020 mPopExitAnim=#7f010021
2019-08-15 16:59:30.896 30041-30041/com.app.debug D/FragmentManager: Operations:
2019-08-15 16:59:30.896 30041-30041/com.app.debug D/FragmentManager: Op #0: REPLACE StaffBookingDetailsFragment{82e8301 (97f79b28-d8c1-432a-9e1c-3a781dd42434) id=0x7f0a01c5}
2019-08-15 16:59:30.896 30041-30041/com.app.debug D/FragmentManager: enterAnim=#7f01001e exitAnim=#7f01001f
2019-08-15 16:59:30.896 30041-30041/com.app.debug D/FragmentManager: popEnterAnim=#7f010020 popExitAnim=#7f010021
2019-08-15 16:59:30.896 30041-30041/com.app.debug D/FragmentManager: Op #1: SET_PRIMARY_NAV StaffBookingDetailsFragment{82e8301 (97f79b28-d8c1-432a-9e1c-3a781dd42434) id=0x7f0a01c5}
2019-08-15 16:59:30.897 30041-30041/com.app.debug D/FragmentManager: enterAnim=#7f01001e exitAnim=#7f01001f
2019-08-15 16:59:30.897 30041-30041/com.app.debug D/FragmentManager: popEnterAnim=#7f010020 popExitAnim=#7f010021
2019-08-15 16:59:31.935 30041-30041/com.app.debug D/FragmentManager: mName=4-2131362286 mIndex=-1 mCommitted=false
2019-08-15 16:59:31.935 30041-30041/com.app.debug D/FragmentManager: Operations:
2019-08-15 16:59:31.936 30041-30041/com.app.debug D/FragmentManager: Op #0: REPLACE ParticipantsFragment{fdd9ef9 (b7317713-b150-44a2-8b1c-47a0f8c52781) id=0x7f0a01c5}
2019-08-15 16:59:31.936 30041-30041/com.app.debug D/FragmentManager: Op #1: SET_PRIMARY_NAV ParticipantsFragment{fdd9ef9 (b7317713-b150-44a2-8b1c-47a0f8c52781) id=0x7f0a01c5}
2019-08-15 16:59:55.266 30041-30041/com.app.debug E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.app.debug, PID: 30041
java.lang.IllegalStateException: Fragment ParticipantsFragment{b6e8bc7 (aa204a1e-5f3a-40c0-86f0-b5edab4b07eb)} not associated with a fragment manager.
at androidx.fragment.app.Fragment.requireFragmentManager(Fragment.java:910)
at com.app.bookings.participants.ParticipantsFragment.onParticipantActionClicked(ParticipantsFragment.kt:88)
at com.app.databinding.ItemBindParticipantBindingImpl._internalCallbackOnClick(ItemBindParticipantBindingImpl.java:218)
at com.app.generated.callback.OnClickListener.onClick(OnClickListener.java:11)
at android.view.View.performClick(View.java:6669)
at android.view.View.performClickInternal(View.java:6638)
at android.view.View.access$3100(View.java:789)
at android.view.View$PerformClick.run(View.java:26145)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6863)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
确保片段被垃圾回收/销毁。如果在onCreateView/onViewCreated等方法中注册了任何生命周期未知的注册侦听器(不支持androidx.lifecycle.Lifecycle的侦听器(,则片段不会被垃圾回收/销毁。确保在片段的 onDestroyView(( 中取消注册此类侦听器。
例:OnBackPressedDispatcher
不是生命周期感知的。因此,它希望您在销毁该片段时取消注册。如果它没有被取消注册,那么它会保留一个引用,并在其他片段中按下 back 时被调用。
我在里面调用findNavController((.navigateUp((
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
//some logic that needs to be run before fragment is destroyed
findNavController().navigateUp()
}
}
requireActivity().onBackPressedDispatcher.addCallback(
onBackPressedCallback
)
}
如果你看一下 findNavController(( 的文档
在不是 [NavHostFragment] 或 [NavHostFragment] 的片段上调用它将导致 [IllegalStateException]
这就是为什么我得到
非法状态异常片段未与片段管理器关联
解决方案:
在 onDestroyView 中注销侦听器
override fun onDestroyView() {
super.onDestroyView()
//unregister listener here
onBackPressedCallback.isEnabled = false
onBackPressedCallback.remove()
}
如果添加生命周期所有者,则无需删除onDestroy
文档中的回调
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
findNavController().navigateUp()
}
}
// ADD LIFECYCLE OWNER
requireActivity().onBackPressedDispatcher.addCallback(this,
onBackPressedCallback
)
}
可以使用导航操作的 XML 属性popUpToInclusive="true"
来指定是否应弹出同一目标的旧实例。 另请参阅文档
Navigation.findNavController(it(.navigate(R.id.action_participants(
而不是上面的地方
Navigation.findNavController(context(.navigate(R.id.action_participants(
我发现你使用了viewbinding。此类是在适配器中创建的,不能直接使用 findnavcontroller。您需要将当前片段传递给适配器,然后将过去的片段传递给所需的类。如果您需要活动,也是如此。将 requireActivity 传递给适配器,然后传递给所需的类
@Ramakrishna Joshi是正确的。就我而言,问题是我正在使用
val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(menuProvider)
所以我不得不像这样在 onDestroyView 上删除它
override fun onDestroyView() {
val menuHost: MenuHost = requireActivity()
menuHost.removeMenuProvider(menuProvider)
super.onDestroyView()
}
经过进一步调查,我验证了这只是碎片处理不当时的副作用。现在解决了。