Fragment 的生命周期与宿主 Activity 的生命周期有何关联和差异?

解读

国内面试中,这道题几乎必问,但大多数候选人只背“onAttach→onCreateView→onDestroyView→onDetach”顺序,却说不清“为什么 Fragment 比 Activity 多两根生命周期轴”以及“两根轴如何被宿主 Activity 的 Re-create 触发”。面试官真正想听的是:

  1. 你能把 Fragment 的“自身生命周期”与“视图生命周期”拆成两条线,并指出它们与 Activity 的对应节点如何错位;
  2. 你能解释在屏幕旋转、内存重启、ViewPager 延迟加载、setMaxLifecycle、setRetainInstance(true) 等国产机型常见场景下,两条轴与 Activity 的同步/异步表现;
  3. 你能给出实战代码片段,证明你知道在 onDestroyView 里清引用、在 onCreate 里做恢复、在 onAttach 里判 Context 类型,避免国内厂商 ROM 带来的 Crash。

知识点

  1. Fragment 双轴生命周期
    1.1 自身轴:onAttach → onCreate → onCreateView → onViewCreated → onStart → onResume → onPause → onStop → onDestroyView → onDestroy → onDetach
    1.2 视图轴:onCreateView → onViewCreated → onDestroyView(与自身轴可多次交叉)
  2. Activity 单轴生命周期:onCreate → onStart → onResume → onPause → onStop → onDestroy
  3. 关联规则
    3.1 Fragment 的 onAttach/onCreate 只在 Activity onCreate 之后调用一次;
    3.2 Fragment 的 onStart/onResume 在 Activity 对应节点之后、但可能分批(ViewPager+BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
    3.3 Fragment 的 onPause/onStop 在 Activity 对应节点之前、也可能分批;
    3.4 Fragment 的 onDestroyView 可以早于 Activity onDestroy 多次执行(回退栈弹出、ViewPager 缓存淘汰);
    3.5 Fragment 的 onDestroy/onDetach 一定在 Activity onDestroy 之前完成,但 setRetainInstance(true) 时 onDestroy 会跳过,onDetach 仍在 Activity onDestroy 前执行。
  4. 差异点
    4.1 视图可销毁再重建,自身不销毁(回退栈、ViewPager 缓存);
    4.2 setRetainInstance(true) 让自身轴跳过 onDestroy/onCreate,但视图轴仍完整走;
    4.3 setMaxLifecycle(Lifecycle.State.STARTED) 可把 Fragment 的 resume 状态卡在 Activity 之下,用于防闪退、省流量,是国内资讯类 App 标配;
    4.4 国产 ROM 后台冻结策略会让 Activity onStop 后不再回调 onSaveInstanceState,导致 Fragment 的 mSavedViewState 为 null,需在 onAttach 里做判空恢复。
  5. 高频坑
    5.1 在 onCreateView 里做 addToBackStack 导致 IllegalStateException;
    5.2 在 onDestroyView 里没把 ViewBinding/RecyclerView.Adapter 置空,被复用触发空指针;
    5.3 在 Activity onBackPress 里直接 finish 而未 popBackStack,导致 Fragment onDestroy 没走,LeakCanary 报内存泄漏;
    5.4 使用 FragmentResultListener 时,在 onCreate 注册、在 onDestroy 移除,否则旋转后重复注册,国产机型必现 Crash。

答案

Fragment 生命周期与 Activity 生命周期呈“双轴对单轴”的耦合关系:

  1. 自身轴与 Activity 轴同步创建、异步销毁。Activity onCreate 触发 FragmentController 的 dispatchCreate,从而批量执行 Fragment onAttach/onCreate;Activity onDestroy 前,FragmentManager 先执行所有 Fragment 的 onDestroy/onDetach,保证资源释放顺序。
  2. 视图轴与 Activity 轴可多次错位。Activity 在 onCreate 后只触发一次 Fragment onCreateView,但后续因回退栈弹出、ViewPager 缓存回收、replace+addToBackStack 等操作,Fragment 可以重复执行 onCreateView/onDestroyView,而自身轴保持存活;因此视图层引用(ViewBinding、RecyclerView.Adapter、动画 Drawable)必须在 onDestroyView 里及时置空,避免内存泄漏与“Can not perform this action after onSaveInstanceState”异常。
  3. 状态恢复节点错位。Activity 在 onSaveInstanceState 中把 mFragments.saveAllState 写入 Parcelable,Fragment 的 onSaveInstanceState 在 Activity onSaveInstanceState 期间调用;但国产 ROM 常在后台冻结阶段跳过该回调,因此 Fragment 重建时需双重判空:既判 savedInstanceState 是否含 FragmentManagerState,也判 getArguments() 是否含业务缓存,防止白屏。
  4. 延迟加载与 setMaxLifecycle。国内主流 App 使用 ViewPager2+FragmentStateAdapter,并配合 setMaxLifecycle(Lifecycle.State.STARTED) 让不可见 Fragment 只走到 onStart,不走到 onResume,节省埋点与网络请求;此时 Fragment 的 onResume 真正触发时机在页面选中后,与 Activity onResume 已完全解耦,需在 onResume 里做可见性埋点,避免 PV 统计漏报。
  5. setRetainInstance 特例。在横竖屏切换频繁的车机项目中,用 setRetainInstance(true) 让 Fragment 自身轴跳过 onDestroy/onCreate,但视图轴仍完整重建,因此背景播放器、蓝牙连接等长生命周期对象可放在 Fragment 成员变量,而 SurfaceView/TextureView 仍在 onCreateView/onDestroyView 中重建,兼顾性能与稳定性。

一句话总结:Activity 生命周期是“单轴到底”,Fragment 生命周期是“自身轴+视图轴”双轴交叉;视图轴可随回退栈与配置变化多次重建,而自身轴与 Activity 保持“同生晚死”的同步,但 setRetainInstance 与 setMaxLifecycle 会让同步点产生错位,面试时必须结合代码场景说明如何防泄漏、防 Crash、保数据。

拓展思考

  1. AndroidX 的 Fragment 1.3+ 引入 FragmentResult API,生命周期感知器在 CREATED 状态注册,若宿主 Activity 被国产 ROM 强制回收,Fragment 重建后原回调对象已失效,如何设计“粘性结果”机制保证业务不丢单?
  2. Jetpack Navigation 通过 NavHostFragment 把回退栈托管到 NavController,其生命周期与原生 FragmentManager 差异在哪?在深层链接场景下,NavHostFragment 的 onCreate 会提前创建所有目标 Fragment,但只走 onCreate 不走 onCreateView,如何利用这一点做秒开优化?
  3. 折叠屏展开/折叠会触发 Activity 的 configChange 但不重建,若 Fragment 声明了 android:configChanges="screenSize|smallestScreenSize",其视图轴是否重建?如何在 onConfigurationChanged 中只刷新 RecyclerView 的 LayoutManager 而避免整页闪白?
  4. 国内厂商推送保活插件常把 Activity 设为 singleInstance 并透明悬浮,Fragment 在这种“无视图窗口”中生命周期如何表现?是否会出现 onResume 已回调但 Window 的 DecorView 未 attach 导致 DialogFragment 抛“token is null”?如何通过 Hook FragmentManagerImpl 提前拦截?