Android OnBackPressedDispatcher:从原理到实战,解决复杂返回逻辑

📅 2026/8/4 8:43:56
Android OnBackPressedDispatcher:从原理到实战,解决复杂返回逻辑
1. 项目概述为什么我们需要OnBackPressedDispatcher在Android开发中处理返回键或手势返回的逻辑几乎是每个应用都绕不开的环节。传统的做法是什么在Activity里重写onBackPressed()方法或者在Fragment里通过onBackPressedDispatcher添加回调。听起来很简单对吧但实际项目中尤其是当你的应用架构变得复杂引入了单Activity多Fragment、导航组件Navigation、或者需要处理嵌套Fragment、对话框、底部弹窗的返回逻辑时传统的处理方式很快就会变成一团乱麻。你可能会遇到这样的场景一个Activity承载了多个Fragment栈某个深层级的Fragment需要拦截返回键执行自己的逻辑比如先保存草稿而它上层的DialogFragment又希望在返回时先关闭自己。或者在使用了Jetpack Navigation的图中你希望在某些条件下阻止用户通过返回键退出当前目的地。这些需求交织在一起如果只用简单的onBackPressed()重写代码会充满各种if-else判断和状态标志难以维护且容易出错。这就是OnBackPressedDispatcher的价值所在。它并不是一个全新的概念但却是Android官方在Jetpack组件中提供的一套更现代化、更声明式、也更可控的返回事件处理机制。它的核心思想是将返回事件的处理“分发”出去让各个组件如Fragment、Dialog能够以可观察、可组合、且生命周期感知的方式声明自己对返回事件的“兴趣”和处理权。简单说它把原来集中在Activity一处的“独裁”逻辑变成了一个可以“协商”的“议会”系统。对于中级及以上开发者而言深入理解并熟练运用OnBackPressedDispatcher是构建健壮、可维护的Android UI层交互逻辑的关键一步。它不仅能帮你优雅地解决复杂的返回拦截需求其设计思想也对理解Android最新的架构模式大有裨益。接下来我们就从设计思路开始彻底拆解它。2. 核心设计思路与机制解析要用好OnBackPressedDispatcher不能只停留在API调用的层面必须理解其背后的设计理念和运行机制。这能帮助你在遇到复杂情况时做出正确的设计和排查问题。2.1 从“独裁”到“协同”的范式转变在OnBackPressedDispatcher出现之前处理返回事件是典型的“命令式”和“集中式”范式。Activity的onBackPressed()方法是一个单一的入口点所有拦截逻辑都必须写在这里。这带来了几个问题职责过重Activity需要了解所有子组件Fragment、View的返回逻辑违反了单一职责原则。耦合度高子组件需要将自己的状态或回调接口暴露给Activity增加了组件间的耦合。顺序管理困难当多个组件都需要拦截返回时谁先谁后这个顺序逻辑硬编码在Activity中难以动态调整。生命周期不匹配子组件如Fragment可能已经销毁但Activity中的回调引用可能还未清除导致内存泄漏或空指针异常。OnBackPressedDispatcher引入了“响应式”和“分散式”的范式。它的核心是一个在Activity中管理的分发器Dispatcher但处理逻辑的“消费者”OnBackPressedCallback可以来自任何生命周期所有者如Fragment、DialogFragment。每个OnBackPressedCallback都可以独立地设置自己是否处于“启用”enabled状态并定义自己被调用时要执行的逻辑。当用户按下返回键时分发器会按照回调添加的“顺序”依次询问那些处于“启用”状态的回调“你要处理这个返回事件吗”第一个声称要处理其handleOnBackPressed方法被调用的回调将“消费”掉这个事件流程就此终止。如果所有已启用的回调都不处理那么最终会执行默认行为通常是Activity的finish()或Fragment的回退。这种模式的好处显而易见解耦组件只需关注自己的回调无需知道其他组件的存在。动态性回调的启用/禁用状态可以随时根据组件状态如数据是否已保存改变。顺序可控通过控制回调添加的顺序通常与UI层级相关可以自然地实现处理优先级。生命周期安全通过addCallback(LifecycleOwner, OnBackPressedCallback)方法添加的回调会在LifecycleOwner销毁时自动移除从根本上避免了内存泄漏。2.2 关键组件深度剖析1. OnBackPressedDispatcher这是系统的调度中心通常通过Activity.getOnBackPressedDispatcher()或Fragment.getOnBackPressedDispatcher()获取。注意Fragment也有自己的分发器但它最终会委托给Activity的分发器同时会考虑Fragment在返回栈中的位置这是一个重要的细节我们后面会讲到。它的主要职责是维护一个OnBackPressedCallback的有序集合并在返回事件触发时遍历这个集合。2. OnBackPressedCallback这是真正处理返回事件的单元。它是一个抽象类你需要创建它的实现通常用匿名内部类或Kotlin的SAM转换。val callback object : OnBackPressedCallback(enabled false) { override fun handleOnBackPressed() { // 你的处理逻辑例如显示一个确认对话框 showSaveConfirmationDialog() } }构造参数enabled初始的启用状态。通常根据组件初始化时的状态来设置。例如一个编辑页面如果内容有改动则初始为true需要拦截否则为false。handleOnBackPressed()方法当该回调被选中处理返回事件时会调用此方法。重要这个方法被调用并不意味着你一定要“消费”这个事件。理论上你可以在这里什么都不做或者只执行一些副作用如记录日志然后让事件继续传递。但通常这里就是执行拦截逻辑如保存数据、弹出提示的地方。isEnabled属性这是一个可观察的observable属性。你可以随时通过callback.isEnabled true/false来改变它的状态。UI组件如按钮可以监听这个状态来更新自己的外观例如保存按钮在isEnabled为true时高亮。3. LifecycleOwner这是将回调与生命周期绑定的关键。通过dispatcher.addCallback(lifecycleOwner, callback)添加的回调其生命周期将与传入的lifecycleOwner通常是Fragment或Activity绑定。当LifecycleOwner被销毁ON_DESTROY时该回调会自动从分发器中移除。这是官方推荐的最佳实践能有效避免内存泄漏。2.3 处理流程与优先级模型当返回事件发生时OnBackPressedDispatcher的内部处理流程可以简化为以下步骤事件触发用户按下物理返回键或触发了手势返回。获取回调列表分发器获取当前所有已注册且isEnabled为true的OnBackPressedCallback。注意顺序回调是按照添加的顺序排列的后添加的回调默认拥有更高的优先级会被先询问。这一点非常关键因为它通常对应着UI层级的“从顶到底”。例如最后显示的对话框添加的回调应该最先被检查。顺序询问分发器从列表的末尾开始向前遍历即优先级最高的先被检查。处理或传递如果当前回调的handleOnBackPressed()方法被调用并且该方法正常执行完毕没有抛出异常则认为该回调已“消费”了返回事件流程立即终止。如果该回调的handleOnBackPressed()方法内部没有消费事件例如它只是记录日志然后什么也不做理论上事件应该继续传递这里有个重要细节在标准的OnBackPressedDispatcher实现中一旦一个启用的回调的handleOnBackPressed被调用无论其内部逻辑如何事件都不会再传递给列表中的其他回调。也就是说每个启用的回调在一次返回事件中最多被“询问”一次且第一次被询问的调用就是最终调用。所以你的回调逻辑必须在handleOnBackPressed内做出最终决定。执行默认行为如果遍历完所有已启用的回调没有任何一个回调的handleOnBackPressed被调用或者所有回调的isEnabled都是false那么分发器将执行“默认返回操作”。对于Activity的分发器这通常是调用Activity.onBackPressed()的默认实现即super.onBackPressed()可能会触发Fragment回退或finish Activity。对于Fragment其默认行为是尝试从返回栈中弹出。实操心得理解“后添加先调用”的优先级模型至关重要。在添加回调时你必须考虑UI的叠加顺序。例如一个全屏的Fragment应该比一个覆盖在其上的DialogFragment更早添加回调即DialogFragment的回调后添加优先级更高这样DialogFragment才能先拦截返回事件来关闭自己。3. 核心使用场景与代码实战理解了原理我们来看具体怎么用。我会通过几个逐渐复杂的场景展示如何将OnBackPressedDispatcher集成到你的代码中。3.1 基础用法在Fragment中拦截返回这是最常见的场景。假设我们有一个EditNoteFragment用户在编辑笔记时如果内容有修改点击返回需要提示保存。步骤1创建并管理Callback在Fragment的onCreate或onViewCreated中创建回调。回调的启用状态应该与“数据是否脏已修改”这个状态绑定。class EditNoteFragment : Fragment() { private var isDataDirty false private lateinit var backPressedCallback: OnBackPressedCallback override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 步骤1: 创建Callback初始状态根据数据是否脏决定 backPressedCallback object : OnBackPressedCallback(isDataDirty) { override fun handleOnBackPressed() { // 步骤2: 当返回被拦截时执行自定义逻辑 showSaveConfirmationDialog() } } // 步骤3: 将Callback与Fragment的生命周期绑定 requireActivity().onBackPressedDispatcher.addCallback(this, backPressedCallback) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 监听EditText的改动更新数据脏状态和Callback的启用状态 binding.editText.doAfterTextChanged { text - isDataDirty text.toString() ! originalContent backPressedCallback.isEnabled isDataDirty // 动态更新 } } private fun showSaveConfirmationDialog() { MaterialAlertDialogBuilder(requireContext()) .setTitle(保存更改) .setMessage(笔记内容已修改是否保存) .setPositiveButton(保存) { _, _ - saveNoteAndPopBackStack() } .setNegativeButton(不保存) { _, _ - popBackStackIgnoringChanges() } .setNeutralButton(取消) { dialog, _ - dialog.dismiss() } // 取消操作什么都不做停留在当前页 .show() } private fun saveNoteAndPopBackStack() { // 保存逻辑... findNavController().popBackStack() } private fun popBackStackIgnoringChanges() { findNavController().popBackStack() } }关键点解析生命周期绑定addCallback(this, backPressedCallback)中的this指代当前Fragment作为LifecycleOwner。这确保了当Fragment被销毁时回调自动移除。动态启用通过监听EditText的变化实时更新backPressedCallback.isEnabled。这是响应式编程的体现UI状态与返回行为紧密联动。handleOnBackPressed中的逻辑这里我们弹出了一个对话框。注意对话框的“取消”按钮它只是关闭对话框并没有消费返回事件。但是由于这次返回事件已经触发了这个回调的handleOnBackPressed方法事件已被视为由该回调处理。用户再次按下返回键时会再次触发这个回调因为isEnabled仍然为true再次弹出对话框。这个行为是符合预期的确保了拦截的持续性。3.2 进阶场景处理嵌套Fragment与Dialog当UI层级变深时优先级管理就变得重要。假设场景是Activity-MainFragment-EditNoteFragment然后在EditNoteFragment上弹出了一个全屏的ExplanationDialogFragment。我们希望的行为是先检查ExplanationDialogFragment是否需要处理返回比如它内部有可滚动内容第一次返回是滚动到顶部第二次返回才关闭。如果ExplanationDialogFragment不处理再检查EditNoteFragment的保存拦截逻辑。最后才是MainFragment或Activity的默认返回。实现要点DialogFragment中的Callback应该在DialogFragment的onCreateDialog或onViewCreated中添加其回调并且要使用requireActivity().onBackPressedDispatcher。因为DialogFragment本身可能没有自己的分发器或者其分发器与Activity的相连。class ExplanationDialogFragment : DialogFragment() { private var shouldHandleBack true // 模拟内部状态第一次返回滚动第二次关闭 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val callback object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { if (shouldHandleBack) { // 第一次返回执行滚动逻辑 binding.scrollView.smoothScrollTo(0, 0) shouldHandleBack false // 可以在这里更新一些UI提示比如隐藏“滚动到顶部”的提示 } else { // 第二次返回关闭对话框 dismiss() } } } requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback) // 注意这里使用了viewLifecycleOwner它比Fragment的lifecycle更精确 // 会在View销毁时onDestroyView就移除回调避免Dialog视图销毁后回调仍被持有。 } }优先级自动生效由于ExplanationDialogFragment的UI在最顶层它的onViewCreated也就是添加回调的时机发生在EditNoteFragment之后。因此它的回调后添加优先级更高会被先检查。这正是我们想要的行为。注意事项对于DialogFragment强烈建议使用viewLifecycleOwner而不是this来添加回调。因为DialogFragment的视图可能比Fragment本身更早被销毁例如在配置变更时。使用viewLifecycleOwner可以确保回调在视图销毁时及时清理避免不必要的拦截逻辑在不可见的视图上执行。3.3 与Jetpack Navigation深度集成如果你使用Jetpack Navigation组件OnBackPressedDispatcher可以与你现有的导航图NavGraph无缝协作。实际上Navigation库内部就使用了OnBackPressedDispatcher来实现某些特性。一个常见需求是在导航到某个目标Destination时希望禁用系统的返回手势Edge-to-Edge back gesture或物理返回键强制用户完成某个操作比如观看一个强制引导页。方法使用NavController.addOnDestinationChangedListener动态管理Callback你可以在Activity或顶级Fragment中监听导航目标的变化根据目标ID来启用或禁用全局的返回拦截。class MainActivity : AppCompatActivity() { private lateinit var navController: NavController private lateinit var mandatoryGuideCallback: OnBackPressedCallback override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) navController findNavController(R.id.nav_host_fragment) mandatoryGuideCallback object : OnBackPressedCallback(false) { // 初始禁用 override fun handleOnBackPressed() { // 在强制引导页拦截返回并给出提示 Toast.makeText(thisMainActivity, 请先完成引导, Toast.LENGTH_SHORT).show() } } // 将回调添加到Activity的分发器 onBackPressedDispatcher.addCallback(this, mandatoryGuideCallback) navController.addOnDestinationChangedListener { _, destination, _ - // 当导航到ID为 mandatory_guide_fragment 的目标时启用拦截 val shouldIntercept destination.id R.id.mandatory_guide_fragment mandatoryGuideCallback.isEnabled shouldIntercept } } }更优雅的方式使用AppBarConfiguration与OnBackPressedCallback对于带有顶部AppBar的界面你可能会用AppBarConfiguration设置哪些目标不需要显示“向上”按钮。你可以将类似的逻辑应用于返回拦截。val appBarConfiguration AppBarConfiguration.Builder( R.id.home_fragment, // 主页不需要拦截返回可能直接退出App R.id.mandatory_guide_fragment // 引导页需要拦截 ).build() // 在Activity中可以结合appBarConfiguration来管理Callback navController.addOnDestinationChangedListener { _, destination, _ - val isTopLevelDestination appBarConfiguration.topLevelDestinations.contains(destination.id) // 如果不是顶级目的地且是特定需要拦截的页面则启用拦截 mandatoryGuideCallback.isEnabled !isTopLevelDestination destination.id R.id.mandatory_guide_fragment }4. 高级技巧、疑难杂症与性能优化掌握了基本和进阶用法后我们来看看一些更深入的话题这些往往是实战中容易踩坑的地方。4.1 处理多个Callback的复杂交互当多个Callback同时处于启用状态时你需要非常清楚它们的执行顺序和交互逻辑。假设一个页面同时存在Callback A处理未保存数据提示。Callback B处理一个可关闭的侧边抽屉导航栏Drawer。理想情况是当抽屉打开时返回键应该先关闭抽屉而不是触发数据保存提示。这意味着Callback B抽屉的优先级必须高于Callback A数据保存。实现策略顺序添加确保添加抽屉Callback的代码在添加数据保存Callback之后执行。由于后添加的优先级高抽屉Callback会先被检查。状态联动在抽屉打开时可以暂时禁用数据保存Callback避免不必要的检查。但这需要谨慎因为要确保抽屉关闭后能正确恢复。// 在Fragment或Activity中 val saveCallback object : OnBackPressedCallback(false) { /*...*/ } val drawerCallback object : OnBackPressedCallback(false) { override fun handleOnBackPressed() { binding.drawerLayout.closeDrawer(Gravity.START) } } // 监听抽屉状态 binding.drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener { override fun onDrawerOpened(drawerView: View) { drawerCallback.isEnabled true saveCallback.isEnabled false // 抽屉打开时禁用保存提示 } override fun onDrawerClosed(drawerView: View) { drawerCallback.isEnabled false saveCallback.isEnabled isDataDirty // 抽屉关闭后恢复保存提示的状态 } // ... 其他方法 })4.2 Fragment的getOnBackPressedDispatcher()与 Activity的有何不同调用Fragment.getOnBackPressedDispatcher()返回的并不是一个新的分发器而是Activity分发器的一个“包装器”。这个包装器会考虑当前Fragment在返回栈中的状态。核心区别当你通过Fragment的dispatcher添加回调时只有当该Fragment位于返回栈的顶部即当前用户可见时其添加的回调才会被纳入考虑范围。如果该Fragment因为导航到其他目标而被置于后台onStop那么它注册的回调将自动失效即使isEnabledtrue直到它再次回到前台。这个特性非常有用它天然地解决了“非活跃Fragment不应拦截返回事件”的问题。你不需要手动在onPause/onResume中管理回调的启用状态。结论在Fragment中**优先使用getOnBackPressedDispatcher()**而不是requireActivity().onBackPressedDispatcher。前者提供了基于生命周期的自动作用域管理更安全、更符合直觉。4.3 内存泄漏与生命周期陷阱虽然使用addCallback(LifecycleOwner, callback)可以自动移除回调但仍有几个陷阱需要注意错误的使用viewLifecycleOwner在Fragment的onCreate或onActivityCreated中使用viewLifecycleOwner会导致崩溃因为此时Fragment的视图可能还未创建。viewLifecycleOwner只能在onViewCreated之后使用。在onCreate中应该使用thisFragment本身作为LifecycleOwner。在DialogFragment中使用this如前所述在DialogFragment中如果回调逻辑依赖于视图应使用viewLifecycleOwner。如果回调逻辑与视图无关例如阻止在任何情况下关闭对话框可以使用this。持有外部引用在OnBackPressedCallback的实现中如果持有了Activity、Fragment或View的强引用即使使用了生命周期绑定在回调对象本身被其他长生命周期对象持有时也可能造成泄漏。确保回调本身是轻量的或者使用弱引用。4.4 测试策略测试OnBackPressedDispatcher的逻辑至关重要。你可以使用androidx.activity:activity-testing和androidx.fragment:fragment-testing库中的工具进行测试。核心测试思路模拟返回键按压使用Espresso的pressBack()动作。验证回调状态你可以获取OnBackPressedDispatcher并检查其内部回调集合通过反射不推荐用于生产代码但测试中可用或者通过更黑盒的方式验证触发返回后预期的UI行为是否发生如对话框弹出、页面导航。测试生命周期绑定在测试中销毁并重新创建Activity/Fragment验证回调是否被正确清理和重新添加。RunWith(AndroidJUnit4::class) class EditNoteFragmentTest { Test fun backPressed_whenDataIsDirty_showsConfirmationDialog() { // 启动Fragment到测试Activity中 val scenario launchFragmentInContainerEditNoteFragment() scenario.onFragment { fragment - // 模拟数据变脏 fragment.isDataDirty true fragment.backPressedCallback.isEnabled true } // 按下返回键 Espresso.pressBack() // 验证确认对话框是否显示例如检查特定的TextView是否出现在屏幕上 onView(withText(保存更改)).check(matches(isDisplayed())) } }5. 常见问题排查与实战记录即使理解了原理在实际编码中还是会遇到各种奇怪的问题。下面是我在项目中遇到的一些典型问题及解决方案。5.1 问题速查表问题现象可能原因解决方案回调根本没有被触发1. Callback的isEnabled属性为false。2. 添加Callback的Fragment不在返回栈顶部使用了Fragment的dispatcher。3. 添加Callback的LifecycleOwner已经处于非活跃状态如onStop。4. 有更高优先级的Callback消费了事件。1. 检查并确保在需要拦截时isEnabled true。2. 确认Fragment的导航状态或考虑使用Activity的dispatcher。3. 检查添加回调的时机确保在活跃生命周期如onStart之后。4. 检查其他Callback的优先级和启用状态。回调被触发一次后再次按返回键无效在handleOnBackPressed()方法中可能调用了finish()或popBackStack()导致LifecycleOwner被销毁Callback被自动移除。如果希望回调持续有效确保在handleOnBackPressed()中不要立即销毁宿主。例如先弹出对话框在对话框回调中再执行销毁操作。DialogFragment关闭后底层的Fragment拦截逻辑不生效DialogFragment添加回调时使用了requireActivity().onBackPressedDispatcher且没有在Dialog关闭后正确移除或禁用其回调。在DialogFragment中使用viewLifecycleOwner添加回调。当Dialog视图销毁时回调会自动移除。或者在dismiss()后手动将回调的isEnabled设为false。使用了addCallback但出现内存泄漏警告可能使用了错误的LifecycleOwner如在Fragment的onCreateView中使用了viewLifecycleOwner但此时view可能还未创建好。或者在非UI组件如ViewModel中持有了Callback并添加但没有合适的LifecycleOwner来管理其生命周期。遵循生命周期最佳实践在Fragment的onCreate中使用this在onViewCreated及之后使用viewLifecycleOwner。避免在ViewModel等长生命周期组件中直接添加Callback如需添加应传入当前Activity/Fragment的LifecycleOwner。与ViewPager2或ViewPager中的Fragment交互异常ViewPager中的Fragment即使不可见其生命周期也可能并未进入onStop。如果这些Fragment也添加了回调并且isEnabled为true它们可能会错误地拦截返回事件。为ViewPager中的Fragment添加回调时需要更精细地控制isEnabled的状态。通常需要监听ViewPager的页面选择事件只在当前页面启用回调在其他页面禁用。5.2 一个复杂的实战案例协调多个BottomSheetDialogFragment我曾负责一个电商App的商品详情页页面结构复杂主Activity商品详情Fragment (A)从A可以弹出“选择规格”的BottomSheetDialogFragment (B)从B内部又可以弹出“查看尺码表”的另一个BottomSheetDialogFragment (C)需求是返回键应该按C - B - A的顺序依次关闭这些层。最初的错误实现我在A、B、C中都直接用requireActivity().onBackPressedDispatcher.addCallback(this, callback)。结果发现有时按返回键会直接关闭A跳过了B和C。问题根源虽然B和C是后添加的理论上优先级高。但是BottomSheetDialogFragment在显示时底层的A Fragment可能因为配置变更或其他原因经历了onStop-onStart。当A重新onStart时它又添加了一次自己的Callback因为onCreate或onStart中的代码执行了这个新添加的Callback实际上被放在了分发器列表的最后优先级变得比B和C的更高解决方案为A使用Fragment作用域的Dispatcher将A中的回调添加改为getOnBackPressedDispatcher().addCallback(viewLifecycleOwner, callback)。这样当A不可见B或C显示时其回调会自动失效。在B和C中使用Activity的Dispatcher但确保顺序在B和C的onStart或onViewCreated中添加回调。由于它们的UI显示在A之上它们的生命周期方法调用顺序保证了其回调后添加。在B和C关闭时无需手动操作由于使用了viewLifecycleOwner或this作为LifecycleOwner当它们被关闭dismiss时回调会自动移除。最终代码结构// Fragment A class ProductDetailFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // A使用自己的dispatcher只在顶部时生效 val callbackA object : OnBackPressedCallback(false) { /* 处理A的返回如返回列表 */ } // 根据业务逻辑设置callbackA.isEnabled getOnBackPressedDispatcher().addCallback(viewLifecycleOwner, callbackA) } } // BottomSheetDialogFragment B class SkuSelectorBottomSheet : BottomSheetDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // B使用Activity的dispatcher确保优先级高于A val callbackB object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { dismiss() // 关闭自己 } } requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callbackB) } } // BottomSheetDialogFragment C (从B中启动) class SizeChartBottomSheet : BottomSheetDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // C同样使用Activity的dispatcher由于在B之后显示其回调最后添加优先级最高 val callbackC object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { dismiss() // 关闭自己 } } requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callbackC) } }这个案例深刻说明了理解“回调添加顺序”、“Fragment作用域分发器”和“生命周期”三者关系的重要性。