2. Fragment 生命周期深度解析:状态流转、附加/分离、隐藏/显示回调差异

2.1 生命周期核心状态与流转路径

Fragment 的生命周期由 FragmentManager 驱动,与宿主的 Activity 生命周期耦合,但拥有独立的附加/分离、创建/销毁、隐藏/显示回调链。完整状态流转如下:

关键概念对比: Fragment 的“状态”不等同于 Activity 状态,它使用内部枚举 FragmentState(INITIALIZING → CREATED → ACTIVITY_CREATED → STARTED → RESUMED)管理生命周期栈。
Fragment 生命周期状态流转图 Initial(构造) onAttach onCreate onCreateView onViewCreated onActivityCreated onStart onResume onPause onStop onDestroyView onDestroy onDetach 虚线表示返回路径(如 onPause → onResume),实际由 FragmentManager 根据栈状态驱动

2.2 附加/分离回调差异

回调触发条件参数注意点
onAttach(Context)Fragment 被添加到 FragmentManager 并与 Activity 建立关联时Context 对象(API 23+)此时可通过 getActivity() 获取宿主,但 view 尚未创建
onDetach()Fragment 从 FragmentManager 移除,与 Activity 解除关联无调用后 getActivity() 返回 null,所有 UI 引用必须在此前释放
onAttachFragment(Fragment)仅宿主 Activity 回调,通知子 Fragment 被附加Fragment 实例适合 Activity 监听子 Fragment 的附加事件
最佳实践: 在 onDetach() 中清空对 Activity 的弱引用或监听器,避免内存泄漏。切勿在 onDetach() 之后调用 getActivity()。

2.3 隐藏/显示回调差异

Fragment 的隐藏/显示有两种触发路径,对应的回调不同:

  1. FragmentTransaction.hide/show:通过事务操作控制可见性,触发的回调是 onHiddenChanged(boolean hidden)。
  2. View.setVisibility(View.GONE/VISIBLE):直接操作视图可见性,不触发任何 Fragment 生命周期回调,onResume/onPause 也不会调用。
  3. ViewPager + FragmentStatePagerAdapter:通过 setUserVisibleHint(boolean isVisibleToUser) 回调通知可见性变化,需手动实现懒加载。
方法生命周期影响回调序列典型使用场景
transaction.hide(fragment)不触发 onPause/onStop,仅置标记onHiddenChanged(true)Tab 切换、侧滑菜单保留 Fragment 实例
transaction.show(fragment)不触发 onResume,仅置标记onHiddenChanged(false)恢复显示时更新 UI,无需重复创建
直接调用 view.setVisibility(View.GONE)无生命周期回调,Fragment 仍然处于 RESUMED 状态无临时隐藏 UI 但希望保持后台任务运行
setUserVisibleHint(true)仅 ViewPager 场景中由 adapter 调用setUserVisibleHint(需重写)实现 Fragment 的懒加载框架

2.4 状态流转与回调顺序示例

在 Activity 配置变更(旋转屏幕)时,Fragment 经历完整的销毁与重建流程。日志输出示例:

// Activity 旋转时 Fragment 的回调日志(按时间序):
onPause()
onStop()
onSaveInstanceState() -> 保存状态到 Bundle
onDestroyView()
onDestroy()
onDetach()
// 过场后重建:
onAttach(Context)
onCreate(Bundle savedInstanceState)
onCreateView(LayoutInflater, ViewGroup, Bundle)
onViewCreated(View, Bundle)
onActivityCreated(Bundle)
onStart()
onResume()
常见错误: 在 onSaveInstanceState 中保存了对 View 对象的引用,重建后 View 已失效,导致空指针异常。应仅保存序列化数据。

2.5 隐藏/显示回调使用陷阱

  • onHiddenChanged 可能被多次调用:当连续执行 hide() 和 show() 时,只有第一次改变会触发回调,需在回调中判断实际隐藏状态。
  • ViewPager 与 FragmentTransaction 混用:使用 ViewPager 时不要手动调用 hide/show,否则会与 setUserVisibleHint 产生冲突,导致状态不一致。
  • onHiddenChanged 不适用于嵌套 Fragment:父 Fragment 隐藏时,子 Fragment 不会收到 onHiddenChanged,需要递归通知。
调试技巧: 在 Fragment 基类中覆写所有生命周期方法并输出日志,运行 Demo 观察回调顺序。使用 Android Studio 的 “Layout Inspector” 结合 “Logcat” 过滤 FragmentLifecycle 标签可快速定位问题。

2.6 性能与稳定性优化要点

场景优化建议
频繁 hide/show改用 Transaction.setReorderingAllowed(true) 允许合并事务;避免在 onHiddenChanged 中执行耗时操作。
大 Fragment 重建使用 setRetainInstance(true)(已废弃,推荐 ViewModel)或手动在 onSaveInstanceState 中保存不可序列化数据。
嵌套 Fragment 可见性自定义一个可见性监听接口,递归注册,避免遗漏子 Fragment 的隐藏/显示回调。
动画与生命周期协调使用 FragmentTransaction.setCustomAnimations 时,确保动画结束后再执行例如延迟加载等操作,可通过 Animation.AnimationListener 监听。

掌握以上生命周期细节,能有效避免 Fragment 复用和导航栈管理中的状态错乱、内存泄漏以及 UI 闪烁问题。下一环节将基于这些原理构建可复用的 Fragment 基类架构。