学习 Android(十)Fragment的生命周期

[复制链接]
发表于 2025-7-30 01:03:31 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
简介

Android 的 Fragment 是一个具有自己生命周期的 可重用 UI 组件,可以或许在运行时灵活地添加、移除和替换,从而支持单 Activity 多界面、动态布局和相应式设计。把握 Fragment 的生命周期有助于正确地在各个阶段实行初始化、资源绑定、状态生存与释放利用,避免内存泄漏和 UI 崩溃。
1. Fragment 生命周期核心方法

Fragment 的生命周期与 Activity 精密关联,但包罗更多与视图干系的回调:
生命周期方法触发时机用途onAttach()Fragment 与 Activity 关联时获取 Activity 引用,初始化参数onCreate()Fragment 首次创建时(早于视图创建)初始化非视图数据(如数据库查询)onCreateView()创建 Fragment 的视图时加载布局文件(返回 View 对象)onViewCreated()在 onCreateView() 实行完成后获取视图控件引用、配置 RecyclerView 等onActivityCreated()关联的 Activity 已完成 onCreate()确保 Activity 视图停当,实行 Activity 与 Fragment 的交互逻辑onStart()Fragment 可见时(与 Activity 同步)更新 UI 数据onResume()Fragment 可交互时(与 Activity 同步)启动动画、注册传感器监听onPause()Fragment 失去核心时(如跳转其他 Activity)制止耗时利用、生存暂时数据onStop()Fragment 不可见时释放 UI 干系资源onDestroyView()Fragment 视图被移除时(但 Fragment 实例仍存在)清理视图绑定、取消异步任务onDestroy()Fragment 即将被销毁时释放非视图资源onDetach()Fragment 与 Activity 解除关联时清除 Activity 引用2. Fragment 生命周期状态图


3. Fragment 生命周期示例
  1. - app
  2.   - src/main
  3.     - java/com/example/fragmentdemo
  4.       - MainActivity
  5.       - BaseFragment
  6.       - FragmentA
  7.       - FragmentB
  8.     - res/layout
  9.       - activity_main.xml
  10.       - fragment_a.xml
  11.       - fragment_b.xml
复制代码

  • BaseFragment
    1. abstract class BaseFragment : Fragment() {
    2.     protected val TAG: String = "FragmentLifecycle"
    3.     override fun onAttach(@NonNull context: Context) {
    4.         super.onAttach(context)
    5.         Log.d(TAG, "${this::class.java.simpleName} onAttach")
    6.     }
    7.     override fun onCreate(@Nullable savedInstanceState: Bundle?) {
    8.         super.onCreate(savedInstanceState)
    9.         Log.d(TAG, "${this::class.java.simpleName} onCreate")
    10.     }
    11.     override fun onCreateView(
    12.         inflater: LayoutInflater,
    13.         container: ViewGroup?,
    14.         savedInstanceState: Bundle?
    15.     ): View? {
    16.         Log.d(TAG, "${this::class.java.simpleName} onCreateView")
    17.         return inflater.inflate(getLayoutId(), container, false)
    18.     }
    19.     override fun onViewCreated(@NonNull view: View, @Nullable savedInstanceState: Bundle?) {
    20.         super.onViewCreated(view, savedInstanceState)
    21.         Log.d(TAG, "${this::class.java.simpleName} onViewCreated")
    22.     }
    23.     override fun onActivityCreated(@Nullable savedInstanceState: Bundle?) {
    24.         super.onActivityCreated(savedInstanceState)
    25.         Log.d(TAG, "${this::class.java.simpleName} onActivityCreated")
    26.     }
    27.     override fun onStart() {
    28.         super.onStart()
    29.         Log.d(TAG, "${this::class.java.simpleName} onStart")
    30.     }
    31.     override fun onResume() {
    32.         super.onResume()
    33.         Log.d(TAG, "${this::class.java.simpleName} onResume")
    34.     }
    35.     override fun onPause() {
    36.         super.onPause()
    37.         Log.d(TAG, "${this::class.java.simpleName} onPause")
    38.     }
    39.     override fun onStop() {
    40.         super.onStop()
    41.         Log.d(TAG, "${this::class.java.simpleName} onStop")
    42.     }
    43.     override fun onDestroyView() {
    44.         super.onDestroyView()
    45.         Log.d(TAG, "${this::class.java.simpleName} onDestroyView")
    46.     }
    47.     override fun onDestroy() {
    48.         super.onDestroy()
    49.         Log.d(TAG, "${this::class.java.simpleName} onDestroy")
    50.     }
    51.     override fun onDetach() {
    52.         super.onDetach()
    53.         Log.d(TAG, "${this::class.java.simpleName} onDetach")
    54.     }
    55.     /** 子类必须提供此方法来返回布局资源 ID */
    56.     @LayoutRes
    57.     protected abstract fun getLayoutId(): Int
    58. }
    复制代码
  • fragment_a.xml
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3.     xmlns:tools="http://schemas.android.com/tools"
    4.     android:layout_width="match_parent"
    5.     android:layout_height="match_parent"
    6.     tools:context=".FragmentA">
    7.     <TextView
    8.         android:layout_width="match_parent"
    9.         android:layout_height="match_parent"
    10.         android:gravity="center"
    11.         android:textSize="25sp"
    12.         android:textColor="@color/black"
    13.         android:text="This is Fragment A" />
    14. </FrameLayout>
    复制代码
  • fragment_b.xml
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3.     xmlns:tools="http://schemas.android.com/tools"
    4.     android:layout_width="match_parent"
    5.     android:layout_height="match_parent"
    6.     tools:context=".FragmentB">
    7.     <TextView
    8.         android:layout_width="match_parent"
    9.         android:layout_height="match_parent"
    10.         android:gravity="center"
    11.         android:textSize="25sp"
    12.         android:textColor="@color/black"
    13.         android:text="This is Fragment B" />
    复制代码
  ```

  • FragmentA
    1. class FragmentA : BaseFragment() {
    2.     override fun getLayoutId() = R.layout.fragment_a
    3. }
    复制代码
  • FragmentB
    1. class FragmentB : BaseFragment() {
    2.     override fun getLayoutId() = R.layout.fragment_b
    3. }
    复制代码
  • activity_main.xml
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3.     xmlns:tools="http://schemas.android.com/tools"
    4.     android:layout_width="match_parent"
    5.     android:layout_height="match_parent"
    6.     android:orientation="vertical"
    7.     tools:context=".MainActivity">
    8.     <!-- 切换 Fragment 的按钮 -->
    9.     <Button
    10.         android:id="@+id/btn_switch"
    11.         android:layout_width="wrap_content"
    12.         android:layout_height="wrap_content"
    13.         android:text="切换到 FragmentB"
    14.         android:onClick="switchToFragmentB" />
    15.     <!-- Fragment 容器 -->
    16.     <FrameLayout
    17.         android:id="@+id/fragment_container"
    18.         android:layout_width="match_parent"
    19.         android:layout_height="0dp"
    20.         android:layout_weight="1" />
    21. </LinearLayout>
    复制代码
  • MainActivity
    1. class MainActivity : AppCompatActivity() {
    2.     override fun onCreate(savedInstanceState: Bundle?) {
    3.         super.onCreate(savedInstanceState)
    4.         setContentView(R.layout.activity_main)
    5.         // 首次添加 FragmentA
    6.         supportFragmentManager.beginTransaction()
    7.             .add(R.id.fragment_container, FragmentA())
    8.             .commit()
    9.     }
    10.     fun switchToFragmentB(view: View) {
    11.         supportFragmentManager.beginTransaction()
    12.             .replace(R.id.fragment_container, FragmentB())
    13.             .commit()
    14.     }
    15. }
    复制代码

    • 初始加载 FragmentA 的生命周期如下
      1. FragmentA onAttach
      2. FragmentA onCreate
      3. FragmentA onCreateView
      4. FragmentA onViewCreated
      5. FragmentA onActivityCreated
      6. FragmentA onStart
      7. FragmentA onResume
      复制代码
    • 切换 FragmentB 不加入返回栈的生命周期如下
      1. FragmentA onPause
      2. FragmentA onStop
      3. FragmentB onAttach
      4. FragmentB onCreate
      5. FragmentB onCreateView
      6. FragmentB onViewCreated
      7. FragmentB onActivityCreated
      8. FragmentB onStart
      9. FragmentA onDestroyView
      10. FragmentA onDestroy
      11. FragmentA onDetach
      12. FragmentB onResume
      复制代码
    • 切换 FragmentB 加入返回栈的生命周期如下
      1. fun switchToFragmentB(view: View) {
      2.     supportFragmentManager.beginTransaction()
      3.         .replace(R.id.fragment_container, FragmentB())
      4.         .addToBackStack("fragmentB") // 可选:加入返回栈
      5.         .commit()
      6. }
      复制代码
      1. // 点击切换按钮
      2. FragmentA onPause
      3. FragmentA onStop
      4. FragmentA onDestroyView
      5. FragmentB onAttach
      6. FragmentB onCreate
      7. FragmentB onCreateView
      8. FragmentB onViewCreated
      9. FragmentB onActivityCreated
      10. FragmentB onStart
      11. FragmentB onResume
      12. // 按返回键返回 FragmentA
      13. FragmentB onPause
      14. FragmentB onStop
      15. FragmentB onDestroyView
      16. FragmentB onDestroy
      17. FragmentB onDetach
      18. // FragmentA 重新创建视图
      19. FragmentA onCreateView
      20. FragmentA onViewCreated
      21. FragmentA onActivityCreated
      22. FragmentA onStart
      23. FragmentA onResume
      复制代码

4. 示例生命周期流程图
  1. 启动 Activity
  2. └── 添加 FragmentA
  3.     ├── onAttach
  4.     ├── onCreate
  5.     ├── onCreateView
  6.     ├── onViewCreated
  7.     ├── onActivityCreated
  8.     ├── onStart
  9.     └── onResume
  10. 替换为 FragmentB(无返回栈)
  11. ├── FragmentA.onPause
  12. ├── FragmentA.onStop
  13. ├── FragmentA.onDestroyView
  14. ├── FragmentA.onDestroy
  15. ├── FragmentA.onDetach
  16. └── FragmentB 完整生命周期
  17. 替换为 FragmentB(有返回栈)
  18. ├── FragmentA.onPause
  19. ├── FragmentA.onStop
  20. ├── FragmentA.onDestroyView
  21. └── FragmentB 完整生命周期(除 onDestroy/onDetach)
  22. 按返回键
  23. ├── FragmentB.onPause
  24. ├── FragmentB.onStop
  25. ├── FragmentB.onDestroyView
  26. ├── FragmentB.onDestroy
  27. ├── FragmentB.onDetach
  28. └── FragmentA 重建视图
  29.     ├── onCreateView
  30.     ├── onViewCreated
  31.     ├── onActivityCreated
  32.     ├── onStart
  33.     └── onResume
复制代码
5. 关键结论


  • 视图生命周期

    • onCreateView 和 onDestroyView 控制视图的创建与销毁。
    • 利用 addToBackStack 后,Fragment 实例保留,但视图会被销毁。

  • 状态保留

    • 在 onSaveInstanceState() 生存数据(在 onCreate 中规复)。
    • 视图干系数据应在 onDestroyView 中清理。

  • 最佳实践

    • 初始化数据:在 onCreate(非视图数据)或 onViewCreated(视图干系)。
    • 释放资源:

      • 视图绑定在 onDestroyView 中置空。
      • 背景线程在 onStop 中取消。

    • 避免内存泄漏:在 onDetach 中清除 Activity 引用

6. 常见 Fragment 面试


  • 什么是 Fragment?它与 Activity 有何区别?

    • Fragment 是 Android Support Library(AndroidX)提供的可重用 UI 组件,具有自己独立的生命周期,但必须托管在 Activity 中。
    • 区别:Activity 代表一个完整的屏幕,必须在 AndroidManifest.xml 中声明;而 Fragment 只是屏幕的一部分,可以在运行时动态增删,不需在清单里注册,并支持多个 Fragment 并列显示(如平板双页布局) 。

  • 详细描述 Fragment 的生命周期以及每个回调的作用

    • Fragment 有独立的生命周期回调,与宿主 Activity 生命周期精密关联,其核心序次为:
      1. onAttach() → onCreate() → onCreateView() → onViewCreated() → onStart() → onResume()
      2.      ▲                                    ▼
      3. onPause() ← onStop() ← onDestroyView() ← onDestroy() ← onDetach()
      复制代码

      • onAttach(Context):Fragment 与 Activity 关联时调用,通常获取 Context 或接口回调引用。
      • onCreate(Bundle?):进行全局变量或非视图逻辑初始化,可以调用 setRetainInstance(true) 保留 Fragment 实例。
      • onCreateView(…):创建并返回 UI 布局,实行 inflater.inflate(...)。
      • onViewCreated(View, Bundle?):视图创建完成后调用,安全绑定子视图和注册 LiveData 观察者 。
      • onStart()/onResume():Fragment 可见并得到核心,规复动画或摄像头预览等交互逻辑。
      • onPause()/onStop():制止动画、生存易丢失状态,释放重量级资源(如传感器、广播吸收器)。
      • onDestroyView():销毁视图层次,清理与视图绑定的引用,防止内存泄漏。
      • onDestroy()/onDetach():彻底释放背景资源,并与 Activity 分离 。


  • Fragment 与 Activity 之间怎样转达数据?

    • 通过 setArguments()/getArguments():在创建 Fragment 实例前,调用 fragment.arguments = Bundle().apply { putString("key", value) },在 onCreate() 中读取。此方法包管在重建时数据不会丢失。
    • 宿主 Activity 直接调用公共方法:Activity 在 fragmentManager.findFragmentById() 后,通过类型转换调用 Fragment 的公有方法转达。
    • 共享 ViewModel(保举,Jetpack):Activity 和 Fragment 共享同一个 ViewModel,通过 LiveData 进行双向通讯,无需显式管理 Lifecycle。
    • ragment Result API(AndroidX 1.3+):利用 setFragmentResult()/setFragmentResultListener() 在父 Fragment 或 Activity 间转达数据,更加解耦。

  • 怎样在 Fragment 事务中利用回退栈(Back Stack),以及 add() 与 replace() 的区别?

    • addToBackStack(tag):在调用 .beginTransaction().add(...).addToBackStack(tag).commit() 后,当前事务会被加入回退栈,用户按返回键时可撤销该事务。
    • add():将新 Fragment 覆盖在容器上,但不移除旧 Fragment,可实现多个重叠效果,需手动 hide/show 来管理可见性 。
    • replace():先实行 remove() 再 add(),移除容器内所有旧 Fragment,然后添加新 Fragment,常用于纯粹替换场景 。
    • 回退行为:

      • add() + addToBackStack():回退时会 remove 新 Fragment 并 show 旧 Fragment。
      • replace() + addToBackStack():回退时 remove 替换的 Fragment,并重新 add 之前的 Fragment 实例 。


  • Fragment 状态生存与 setRetainInstance(true) 的作用

    • onSaveInstanceState(Bundle):当 Fragment 被销毁(如配置变化)前,体系会回调此方法。开辟者应在此中生存 UI 状态(如滚动位置、输入内容)到 Bundle。
    • setRetainInstance(true):设置后,在父 Activity 重建(如旋转)时,Fragment 实例不会被销毁,保留成员变量。但仍会销毁/重建视图层次;仅适用于生存非视图状态且慎用,避免与 ViewBinding 冲突。

  • 嵌套 Fragment(Child Fragment)与 getChildFragmentManager() 的利用场景

    • 嵌套 Fragment:在一个 Fragment 内再承载多个子 Fragment,用于实现如选项卡、动态表单多级布局等复杂 UI。
    • 利用 childFragmentManager(而非 parentFragmentManager)进行事务管理,确保子 Fragment 生命周期与父 Fragment 关联,并自动在父销毁时清理子 Fragment。

  • Fragment 性能优化与常见坑

    • 避免过分嵌套:深度嵌套会增加测量与布局开销,发起扁平化布局或利用 ConstraintLayout。
    • 利用 View Binding / Data Binding:淘汰 findViewById,在 onDestroyView() 中将绑定置空避免泄漏。
    • 公道利用事务方式:大量 add/remove/replace 可能产生视图碎片化,考虑利用 show()/hide() 配合复用可提高效率 。
    • 异步加载:在 onCreateView() 只做视图膨胀,耗时利用(网络、数据库)放到 onViewCreated() 后的协程或 RxJava 中处理。
    • 测试:利用 FragmentScenario 在隔离环境下测试生命周期与 UI 交互,包管稳定性。

​
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表