Android Fragment 深度解析:从入门到精通

Android Fragment 深度解析:从入门到精通

目录

  1. 引言:什么是 Fragment?为什么使用它?
  2. Fragment 的核心概念
    • 宿主 Activity
    • FragmentManager
    • FragmentTransaction
  3. Fragment 生命周期详解
    • 生命周期阶段概览
    • 详细回调方法解析 (附“小孩子与家”比喻)
    • Fragment 生命周期与 Activity 生命周期
    • addToBackStack() 对生命周期的影响
    • 配置变更对生命周期的影响
  4. Fragment 的管理与操作
    • 静态添加 Fragment (XML)
    • 动态添加 Fragment (代码)
    • FragmentTransaction 的主要操作
    • 提交事务:commit()commitNow()
  5. Fragment 状态保存与恢复
    • onSaveInstanceState() 的作用与局限
    • ViewModel:推荐的状态管理方案
  6. Fragment 间通信
    • 通过 ViewModel 共享数据
    • 通过 FragmentResultListener (推荐)
    • 通过接口回调 (传统方式)
  7. Fragment 最佳实践与注意事项
    • 资源管理
    • 避免内存泄漏
    • 处理 Context 引用
    • 避免在 onCreateView() 中执行耗时操作
    • 使用 FragmentContainerView

1. 引言:什么是 Fragment?为什么使用它?

Fragment 是 Android 应用程序 UI 的一个模块化部分。它代表了 Activity 中的一个行为或 UI 片段。你可以将多个 Fragment 组合在一个 Activity 中来构建多窗格 UI,也可以在多个 Activity 中重用同一个 Fragment。

为什么使用 Fragment?

  • 模块化和可重用性:将 Activity 的 UI 和行为分解成更小的、可独立管理和重用的模块。例如,一个新闻应用可以有一个 Fragment 显示文章列表,另一个 Fragment 显示文章详情,这两个 Fragment 可以在平板电脑上并排显示,在手机上则分别显示在不同的 Activity 中。
  • 适应不同屏幕尺寸和方向:Fragment 使得在不同设备(手机、平板)和不同屏幕方向(横屏、竖屏)下,通过重新组合 UI 模块来提供不同的用户体验变得更加容易。
  • 更好的用户体验:通过 FragmentManagerFragmentTransaction,可以方便地在运行时添加、移除、替换 Fragment,实现复杂的 UI 导航和交互,而无需切换整个 Activity,从而提供更流畅的用户体验。
  • 简化 Activity 代码:将部分 UI 逻辑从 Activity 中分离出来,使 Activity 变得更轻量,更专注于协调 Fragment。

2. Fragment 的核心概念

宿主 Activity

Fragment 必须嵌入到 Activity 中。这个 Activity 就是 Fragment 的宿主 Activity。Fragment 的生命周期直接受其宿主 Activity 的生命周期影响,不能超越宿主 Activity 的生命周期。Fragment 可以通过 getActivity() 方法获取到宿主 Activity 的引用。

FragmentManager

每个 Activity 都持有一个 FragmentManager 实例,它负责管理 Activity 中的所有 Fragment。你可以通过 getSupportFragmentManager()(对于 AndroidX)或 getFragmentManager()(已弃用)来获取它。FragmentManager 的主要职责包括:

  • 执行 Fragment 事务(添加、移除、替换等)。
  • 管理 Fragment 返回栈。
  • 查找 Activity 中的 Fragment。

FragmentTransaction

FragmentTransactionFragmentManager 的一个 API,用于执行 Fragment 的添加、移除、替换、隐藏、显示等操作。所有的 Fragment 操作都需要通过 FragmentTransaction 来完成,并且这些操作通常是原子性的(要么全部成功,要么全部失败)。

3. Fragment 生命周期详解

Fragment 的生命周期与它所依附的 Activity 的生命周期紧密相关,但它也有自己独立的生命周期回调方法。

生命周期阶段概览

Fragment 的生命周期可以大致分为四个阶段:

  1. 创建阶段 (Creation):Fragment 被创建并与 Activity 关联,其视图被构建。
  2. 运行阶段 (Started/Resumed):Fragment 可见并与用户交互。
  3. 暂停/停止阶段 (Paused/Stopped):Fragment 失去焦点或不可见。
  4. 销毁阶段 (Destruction):Fragment 的视图被销毁,最终 Fragment 实例本身被销毁并与 Activity 解除关联。

详细回调方法解析 (附“小孩子与家”比喻)

想象一下 Fragment 是一个小孩子,而它所依附的 Activity 是这个小孩子的家。小孩子从出生到长大,再到离开家,会经历很多阶段,每个阶段都有它特定的任务和需要注意的事情。

1. 创建阶段 (Creation)

这个阶段是小孩子刚出生,和家(Activity)建立联系,然后开始准备自己的房间和玩具。

  • onAttach(@NonNull Context context)

    • 比喻:小孩子刚出生,被抱到家(Activity)里,第一次和家人(Activity)建立联系。
    • 调用时机:当 Fragment 第一次被添加到 Activity 时,或者当 Fragment 重新依附到 Activity 时,最先被调用。
    • 作用:Fragment 与 Activity 建立关联。此时可以获取到 Activity 的 Context 对象。
    • 通常用于:获取 Activity 实例,进行类型转换(如果 Fragment 需要与特定的 Activity 类型进行通信),或初始化与 Activity 相关的成员变量。
    • 注意:不要在这里进行视图相关的操作,因为视图尚未创建。
  • onCreate(@Nullable Bundle savedInstanceState)

    • 比喻:小孩子虽然在家里了,但还没开始玩,只是在安静地思考自己是谁,准备一些自己的东西(不包括玩具)。
    • 调用时机:在 onAttach() 之后,onCreateView() 之前。
    • 作用:Fragment 实例被创建。进行 Fragment 的非视图初始化,例如变量初始化、数据加载(不涉及 UI 的)、恢复 Fragment 状态。
    • 参数savedInstanceState 包含了 Fragment 上次被销毁时 onSaveInstanceState() 保存的数据。
    • 通常用于:初始化不依赖于视图的数据、设置 setHasOptionsMenu(true)、恢复非视图状态。
  • onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)

    • 比喻:小孩子开始搭建自己的“玩具屋”的骨架。他需要一块地皮(container)和一套工具(inflater)来搭。
    • 调用时机:在 onCreate() 之后调用。
    • 作用:创建并返回 Fragment 的视图层次结构。这是 Fragment 绘制 UI 的地方。
    • 返回值:必须返回 Fragment 的根视图。如果 Fragment 不提供 UI,可以返回 null
    • 通常用于:通过 inflater.inflate() 方法加载布局文件,并返回生成的 View 对象。
    • 注意:不要在这里进行视图的最终初始化(如设置点击监听器),因为视图可能尚未完全添加到父容器中。
  • onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)

    • 比喻:玩具屋的骨架搭好了(onCreateView 完成了),现在可以往里面放家具、装饰,并安排好功能了。
    • 调用时机:在 onCreateView() 返回视图之后,但在视图被添加到父容器之前调用。
    • 作用:视图已经创建完成,可以安全地进行视图相关的初始化。
    • 参数viewonCreateView() 返回的根视图。
    • 通常用于:初始化视图组件(findViewById)、设置监听器、填充数据到视图、启动动画等。
  • onViewStateRestored(@Nullable Bundle savedInstanceState)

    • 比喻:家具都放好了,而且如果之前玩具屋被拆过,现在也恢复了之前家具的摆放位置
    • 调用时机:在 onViewCreated() 之后,onStart() 之前调用。
    • 作用:通知 Fragment 它的所有视图状态已从 savedInstanceState 中恢复。
    • 通常用于:在视图恢复后,进行一些依赖于视图状态的额外初始化。
  • onStart()

    • 比喻:小孩子起床了,穿好衣服,但还没出门玩。他已经可见了,但还没开始和小伙伴互动。
    • 调用时机:在 onViewStateRestored() 之后,当 Fragment 对用户可见时调用。
    • 作用:Fragment 进入可见状态,但可能还未处于前台,无法与用户交互。
    • 通常用于:注册广播接收器、启动动画、初始化需要可见时才能进行的操作。
2. 运行阶段 (Active/Resumed)

这个阶段小孩子已经出门玩了,可以和小伙伴们尽情互动。

  • onResume()
    • 比喻:小孩子出门玩了,可以和小伙伴们尽情互动了!他现在是最活跃的状态。
    • 调用时机:在 onStart() 之后,当 Fragment 变为用户可见并可交互时调用。
    • 作用:Fragment 处于前台,用户可以与之交互。
    • 通常用于:启动相机预览、获取传感器数据、开始播放媒体、恢复 UI 更新等需要 Fragment 处于活动状态的操作。
3. 暂停/停止阶段 (Inactive/Stopped)

这个阶段小孩子可能暂时离开了玩耍的场地,或者回家睡觉了。

  • onPause()

    • 比喻:小孩子玩累了,回家休息一下,但可能还会出去玩。他暂时不能和小伙伴互动了,但可能还在家里能看到外面。
    • 调用时机:当 Fragment 失去焦点,不再与用户交互时(例如,另一个 Fragment 或 Activity 覆盖了它,但它仍然部分可见)。
    • 作用:Fragment 离开前台。
    • 通常用于:保存持久化数据、停止动画、释放占用系统资源(如相机、传感器)的操作,以避免资源浪费和冲突。
  • onSaveInstanceState(@NonNull Bundle outState)

    • 比喻:小孩子知道自己可能要离开家一段时间(或者家可能会被拆掉重建),所以他把重要的玩具收起来,以防回来找不到了。
    • 调用时机:在 onStop() 之前,当 Fragment 可能会被系统销毁以回收资源时(例如,配置变更、系统内存不足)。
    • 作用:允许 Fragment 保存其动态状态,以便在 Fragment 重新创建时恢复。
    • 参数outState 是一个 Bundle 对象,用于存储要保存的数据。
    • 通常用于:保存用户输入、滚动位置等瞬态 UI 状态。
  • onStop()

    • 比喻:小孩子睡着了,完全看不到了。他已经不在玩耍的场地了,也看不见外面了。
    • 调用时机:当 Fragment 完全不可见时(例如,其宿主 Activity 被停止,或 Fragment 被移除并添加到返回栈)。
    • 作用:Fragment 进入不可见状态。
    • 通常用于:注销广播接收器、停止所有后台任务、释放所有不再需要的资源。
4. 销毁阶段 (Destruction)

这个阶段小孩子开始拆掉自己的玩具屋,最终彻底离开家。

  • onDestroyView()

    • 比喻:小孩子把玩具屋里的家具和装饰都收起来了,只剩下空骨架。玩具屋的“灵魂”还在,但已经空了。
    • 调用时机:在 onStop() 之后,当 Fragment 的视图不再需要时调用。
    • 作用:Fragment 的视图层次结构被销毁。
    • 通常用于:清理与视图相关的资源,例如解绑视图、取消对视图的引用,避免内存泄漏。如果 Fragment 被添加到返回栈,那么 onDestroyView() 会被调用,但 onDestroy() 不会。
  • onDestroy()

    • 比喻:小孩子把玩具屋的空骨架也拆了,所有玩具都收起来了。他自己还在家里,但已经没有自己的房间了。
    • 调用时机:在 onDestroyView() 之后,当 Fragment 实例不再需要时调用。
    • 作用:Fragment 实例被销毁,执行最终的清理。
    • 通常用于:清理所有非视图相关的资源,例如取消网络请求、释放数据库连接、取消后台线程等。
  • onDetach()

    • 比喻:小孩子彻底离开了家(Activity),和家人(Activity)断绝了所有联系
    • 调用时机:在 onDestroy() 之后,当 Fragment 与 Activity 解除关联时调用。
    • 作用:Fragment 与 Activity 的连接被断开。
    • 通常用于:清理所有与 Activity 相关的引用,避免内存泄漏。这是 Fragment 生命周期的最后一个回调。

Fragment 生命周期与 Activity 生命周期

  • Fragment 的生命周期不能超越其宿主 Activity 的生命周期。
  • 当 Activity 处于某个生命周期状态时,其内部的 Fragment 不可能处于比 Activity 更“活跃”的生命周期状态。
    • 例如,如果 Activity 处于 onPause() 状态,其 Fragment 不可能处于 onResume() 状态。
    • 如果 Activity 处于 onStop() 状态,其 Fragment 不可能处于 onStart()onResume() 状态。
  • 当 Activity 调用其生命周期方法时,其 FragmentManager 会相应地调用其管理的所有 Fragment 的生命周期方法。

addToBackStack() 对生命周期的影响

当使用 FragmentTransaction.addToBackStack() 方法将 Fragment 事务添加到返回栈时,Fragment 的生命周期会发生一些特殊的变化:

  • 当 Fragment 被替换或移除并添加到返回栈时,它会经历:
    onPause() -> onStop() -> onDestroyView()
    注意onDestroy()onDetach() 不会被调用。这意味着 Fragment 实例仍然保留在内存中,只是其视图被销毁了。
  • 当用户点击返回按钮时,Fragment 会从返回栈中弹出,并重新创建其视图:
    onCreateView() -> onViewCreated() -> onStart() -> onResume()

配置变更对生命周期的影响

配置变更(例如屏幕旋转、键盘可用性变化)会导致宿主 Activity 被销毁并重建。其内部的 Fragment 也会经历完整的销毁和重建过程:

onPause() -> onSaveInstanceState() -> onStop() -> onDestroyView() -> onDestroy() -> onDetach()
然后重新创建:
onAttach() ->

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值