AndroidX Preference中OnBackPressedDispatcher回退异常及原因问询
场景复现
存在Fragment导航链:MainFragment > SettingsFragment > SubSettingsFragment
- 从MainFragment跳转至SettingsFragment:
navController.navigate(MainFragment.toSettingsFragment())
- 从SettingsFragment通过PreferenceHeaderFragmentCompat跳转至SubSettingsFragment:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <Preference android:fragment=".SubSettingsFragment" app:summary="@string/pref_header_summary_permission" app:title="@string/pref_title_permission" /> </PreferenceScreen>
问题现象
处于SubSettingsFragment时按下返回键,直接回到MainFragment,跳过SettingsFragment。PreferenceHeaderFragmentCompat自带的onBackPressedCallback逻辑未生效。
排查过程
查看官方源码发现,onViewCreated方法中通过requireContext()转换为OnBackPressedDispatcherOwner时返回null,导致addCallback未执行:
@CallSuper override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) onBackPressedCallback = InnerOnBackPressedCallback(this) slidingPaneLayout.doOnLayout { onBackPressedCallback!!.isEnabled = slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen } childFragmentManager.addOnBackStackChangedListener { onBackPressedCallback!!.isEnabled = childFragmentManager.backStackEntryCount == 0 } // 此处owner为null val onBackPressedDispatcherOwner = requireContext() as? OnBackPressedDispatcherOwner onBackPressedDispatcherOwner?.let { it.onBackPressedDispatcher.addCallback( viewLifecycleOwner, onBackPressedCallback!! ) } }
将requireContext()替换为requireActivity()后,回退功能恢复正常:
requireActivity().onBackPressedDispatcher.addCallback( viewLifecycleOwner, onBackPressedCallback!! )
提问
为何requireContext() as? OnBackPressedDispatcherOwner会返回null?
Context的类型差异:
Fragment的requireContext()返回的是与当前Fragment关联的Context,但在Preference相关的Fragment(比如PreferenceHeaderFragmentCompat)场景下,这个Context大概率是PreferenceContextWrapper——这是Preference库为适配偏好设置特性包装的Context对象,它并未实现OnBackPressedDispatcherOwner接口,因此强转时返回null。Activity的接口实现:
OnBackPressedDispatcherOwner接口由ComponentActivity(AndroidX中Activity的基类)实现,requireActivity()直接获取Fragment的宿主Activity,它必然是ComponentActivity的子类,因此可以安全转换为OnBackPressedDispatcherOwner,成功添加回退回调。Fragment嵌套场景的影响:
当PreferenceHeaderFragmentCompat作为子Fragment嵌套在其他Fragment中时,requireContext()返回的可能是父Fragment的Context(而非Activity),而父Fragment本身并未实现OnBackPressedDispatcherOwner接口,同样会导致转换失败。
内容的提问来源于stack exchange,提问作者Viewed

