news 2026/6/17 20:06:03

Fragment 全解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Fragment 全解

Fragment 不属于"四大组件"(Activity / Service / BroadcastReceiver / ContentProvider),它没有自己独立的生命周期入口,必须依附在 Activity(或另一个 Fragment)之中。但它太重要了——现代 Android 主流的"单 Activity 架构"正是建立在 Fragment 之上。

一、Fragment:可复用的"界面碎片"

在 Activity 篇 里我们打过一个比方:

Activity 是相框,Fragment 是相框里可以随时替换的照片。

Activity 是用户看到的"窗口",但一个窗口里的内容往往需要拆分复用

  • 手机上是「列表页」和「详情页」两个界面,平板上却希望它们并排显示在同一个屏幕里;
  • 一个底部导航栏 App,点击不同 Tab 切换内容,但顶部栏、底部栏始终不变。

如果一个界面就开一个 Activity,上面这些需求会非常难写。Fragment 就是为此而生:它是一段可复用的、带有自己生命周期和界面的 UI 单元,可以被组合进 Activity,也可以在运行时动态添加、替换、移除。

一个最简单的 Fragment 长这样:

classProfileFragment:Fragment(R.layout.fragment_profile){overridefunonViewCreated(view:View,savedInstanceState:Bundle?){super.onViewCreated(view,savedInstanceState)// 在这里拿到布局里的控件,绑定数据、设置监听valnameText=view.findViewById<TextView>(R.id.name)nameText.text="张三"}}

注意:Fragment不需要AndroidManifest.xml中注册(这点和四大组件完全不同),它由宿主 Activity 在代码或布局中加载。

Fragment 与 Activity 的关系

  • 一个 Activity 可以容纳一个或多个Fragment;
  • Fragment 的生命周期完全依赖于宿主 Activity——Activity 销毁了,里面的 Fragment 也会跟着销毁;
  • 我们日常用的AppCompatActivity→ 继承自FragmentActivity,正是FragmentActivity提供了管理 Fragment 的能力;
  • Jetpack 的Navigation组件就是基于"单 Activity + 多 Fragment"架构设计的。

注意版本与依赖:现在开发应优先使用 AndroidX 的androidx.fragment.app.Fragment,而不是已废弃的平台版android.app.Fragment。本文所有内容都基于 AndroidX Fragment。


二、单 Activity 架构

早期 Android 是"一个界面一个 Activity"。但随着平板、折叠屏、大屏设备出现,这种方式越来越力不从心:

问题纯 Activity 方案Fragment 方案
平板分屏(列表 + 详情并排)难以实现,Activity 无法并排两个 Fragment 放进同一界面即可
界面复用(同一块 UI 多处使用)复制粘贴布局和逻辑写一个 Fragment 到处用
切换内容但保留外壳(底部导航)频繁启动 Activity,外壳重复一个 Activity 壳,切换 Fragment
页面间转场动画受限于 Activity 转场Fragment 事务可灵活控制动画

于是现代应用的主流架构变成了:

一个 Activity 作为"外壳" + 多个 Fragment 作为"内容页",页面跳转交给 Navigation 组件管理。

这就是单 Activity 架构(Single-Activity Architecture),也是 Google 目前推荐的做法。


三、Fragment生命周期

理解 Fragment,最核心的就是它的生命周期。它比 Activity 复杂,原因在于:

Fragment 有"两条命"——它自身的实例(Fragment 对象)和它的视图(View)是分开管理的。

这一点至关重要,后面会专门展开。先看完整的回调顺序。

3.1 创建阶段的回调顺序

从添加到 Activity,到界面显示,Fragment 依次经历:

  1. onAttach()─── Fragment 与宿主 Activity 关联,此时可以拿到Context

  2. onCreate()─── 创建 Fragment 实例,初始化非视图数据(注意:此时还没有 View)

  3. onCreateView()─── 创建并返回 Fragment 的视图(加载布局)

  4. onViewCreated()─── 视图已创建完成,在这里操作控件最安全

  5. onViewStateRestored()─── 恢复视图保存的状态(如输入框文字)

  6. onStart()─── Fragment 可见

  7. onResume()─── Fragment 到达前台,可与用户交互

    ← 用户正常使用中 →

3.2 销毁阶段的回调顺序

  1. onPause()─── 失去前台焦点
  2. onStop()─── 完全不可见
  3. onDestroyView()───销毁视图(但 Fragment 实例还在)
  4. onDestroy()─── 销毁 Fragment 实例
  5. onDetach()─── Fragment 与 Activity 解除关联

3.3 每个回调该做什么?

回调什么时候调用你该做什么
onAttach()与 Activity 关联时获取Context,做与宿主相关的初始化
onCreate()创建实例时初始化与视图无关的数据(如读取参数arguments
onCreateView()需要绘制界面时加载并返回布局(推荐用构造函数传布局 ID 替代手写此方法)
onViewCreated()视图创建完成后绑定控件、设置监听、观察 ViewModel/LiveData
onStart()即将可见注册 UI 相关监听
onResume()到达前台开启动画、请求相机/传感器
onPause()即将失去前台暂停动画、提交临时数据
onStop()完全不可见释放重量级资源
onDestroyView()视图被销毁清理对 View 的引用(如置空 binding),注销视图相关监听
onDestroy()实例被销毁释放剩余资源
onDetach()与 Activity 解除关联清理对 Context 的引用

注意:不要在onCreateView()之前(如onCreate())操作视图控件,因为那时 View 还不存在。绑定控件、设置点击事件等,统一放在onViewCreated()里最安全。


四、Fragment 的"两条命":实例 vs 视图

这是 Fragment 生命周期里最容易出 Bug的地方,必须单独讲清楚。

普通情况下 Fragment 走完整流程没问题。但有一种常见场景会让"实例还活着、视图却已经死了":把 Fragment 放进BackStack(返回栈)后,又导航到另一个 Fragment。

此时旧 Fragment 会执行到onDestroyView()——它的视图被销毁了,但 Fragment 对象本身没有销毁(还留在返回栈里等待回来)。当用户按返回键回到它时,会重新执行onCreateView()创建一个全新的视图,但onCreate()不会再走一遍。

首次进入: onCreate → onCreateView → onViewCreated → ... → onResume 被覆盖: onPause → onStop → onDestroyView ← 视图没了,实例还在 返回回来: onCreateView → onViewCreated → ... → onResume ← 重建了新视图

这带来两个实际后果:

4.1 视图引用必须及时清理(避免内存泄漏)

如果你用 ViewBinding 持有了视图引用,必须在onDestroyView()中置空,否则旧视图无法被回收:

classProfileFragment:Fragment(R.layout.fragment_profile){privatevar_binding:FragmentProfileBinding?=null// 只在视图存在期间有效privatevalbindingget()=_binding!!overridefunonViewCreated(view:View,savedInstanceState:Bundle?){super.onViewCreated(view,savedInstanceState)_binding=FragmentProfileBinding.bind(view)binding.name.text="张三"}overridefunonDestroyView(){super.onDestroyView()_binding=null// 关键:视图销毁了,引用也要清掉}}

4.2 观察数据要用"视图的生命周期"

观察LiveData时,不要this(Fragment 的生命周期),而要用viewLifecycleOwner(视图的生命周期):

overridefunonViewCreated(view:View,savedInstanceState:Bundle?){super.onViewCreated(view,savedInstanceState)// 正确示例:绑定到视图生命周期viewModel.user.observe(viewLifecycleOwner){user->binding.name.text=user.name}}

为什么?如果用this,当 Fragment 视图被销毁(onDestroyView)但实例仍存活时,观察者不会被移除。等数据更新时,回调里访问的却是已被销毁的旧视图,轻则内存泄漏,重则崩溃。viewLifecycleOwner会随视图一起销毁,自动解除观察。


五、FragmentManager 与事务

Activity 用管理多个 Activity;类似地,FragmentManager负责管理一个宿主里的所有 Fragment。

获取方式:

// 在 Activity 中supportFragmentManager// 在 Fragment 中管理它的子 FragmentchildFragmentManager// 在 Fragment 中获取宿主的 FragmentManagerparentFragmentManager

所有对 Fragment 的增删改,都通过一个**事务(Transaction)**完成,并且必须commit()

supportFragmentManager.commit{setReorderingAllowed(true)// 推荐开启,优化生命周期调度replace<ProfileFragment>(R.id.container)// 替换容器里的 FragmentaddToBackStack("profile")// 加入返回栈,按返回键可回退}

5.1 add / replace / remove 的区别

操作含义注意
add()往容器里添加一个 Fragment多次 add 会叠加,旧的仍在
replace()移除容器里现有 Fragment,再添加新的最常用
remove()移除指定 Fragment
show()/hide()隐藏/显示,不销毁视图适合需要保留状态的 Tab 切换

5.2 返回栈(Back Stack)

调用addToBackStack()后,这次事务会被记录到返回栈。用户按返回键时,系统会回退这次事务(撤销 replace,恢复上一个 Fragment),而不是直接退出 Activity。这正是 Fragment 能模拟"页面跳转"的关键。

提示:直接手写FragmentManager事务在简单场景没问题,但页面多、跳转复杂时,强烈建议改用Navigation 组件(见第七节),它把这些事务封装成了可视化的导航图。


六、Fragment 通信

Fragment 应该是自包含、可复用的,所以它不应该直接持有另一个 Fragment 或 Activity 的引用去调用对方方法(那样耦合太紧,复用就破坏了)。Jetpack 推荐了几种解耦的通信方式。

6.1 共享 ViewModel(同一 Activity 内,最推荐)

同一个 Activity 下的多个 Fragment,可以共享作用域为 Activity 的 ViewModel,通过它间接通信:

// 两个 Fragment 都这样获取,拿到的是同一个 ViewModel 实例privatevalsharedViewModel:SharedViewModelbyactivityViewModels()// A Fragment 写入sharedViewModel.select(item)// B Fragment 观察sharedViewModel.selectedItem.observe(viewLifecycleOwner){item->// 收到 A 的选择}

这是平板"列表 + 详情"场景的标准做法:列表 Fragment 选中某项,详情 Fragment 自动更新。

6.2 Fragment Result API(一次性结果传递)

如果只是 A → B 传一个"结果"(类似 Activity Result),用setFragmentResult/setFragmentResultListener

// 接收方(在 onCreate / onViewCreated 中注册监听)setFragmentResultListener("requestKey"){_,bundle->valresult=bundle.getString("data")}// 发送方setFragmentResult("requestKey",bundleOf("data"to"hello"))

6.3 与宿主 Activity 通信

Fragment 需要通知 Activity 时,优先用共享 ViewModel;轻量场景也可以让 Activity 实现一个接口,Fragment 在onAttach()时拿到回调对象。但接口回调方式耦合较高,新代码更推荐 ViewModel 方案。


七、Navigation 组件

手写事务和返回栈在复杂应用里容易出错。JetpackNavigation把这些封装成了一张可视化的导航图(Nav Graph)

  • NavGraph:一张 XML(或 Compose DSL)图,描述有哪些目的地(Fragment)以及它们之间怎么跳;
  • NavHostFragment:放在 Activity 布局里的"容器",所有 Fragment 在此显示;
  • NavController:负责执行跳转、管理返回栈。
// 在 Fragment 中跳转到详情页,并安全地传参findNavController().navigate(HomeFragmentDirections.actionHomeToDetail(itemId=42))

Navigation 的优势:

  • 类型安全传参(Safe Args 插件,编译期检查参数);
  • 自动处理返回栈和返回键;
  • 统一管理转场动画、深层链接(Deep Link);
  • 与底部导航栏、抽屉菜单一键集成。

现状提示:在 Jetpack Compose 中,导航的主体已逐渐变成Navigation Compose(直接在可组合函数之间导航,不再需要 Fragment)。但只要项目还使用传统 View 体系或 Fragment,“Navigation + Fragment” 仍是主流且完全推荐的方案。


八、几种特殊的 Fragment

类型用途
DialogFragment用 Fragment 的方式实现对话框,能正确处理屏幕旋转,推荐替代直接 new 一个Dialog
BottomSheetDialogFragment从底部弹出的面板(来自 Material 库)
PreferenceFragmentCompat快速搭建"设置页",配合 XML 自动生成设置项
NavHostFragmentNavigation 组件的容器 Fragment

参考来源:

  • Android 官方文档 - Fragments
  • Fragment 生命周期
  • 在 Fragment 之间通信
  • Navigation 组件
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/6 10:55:33

降AIGC黑科技揭秘!降AIGC工具终极测评与精准选型工具箱

2026年&#xff0c;学术写作在AI技术的深度渗透下迎来全新变革。随着AIGC检测机制不断升级&#xff0c;论文中的AI痕迹、重复率超标、内容同质化等问题愈发成为学术研究者的“隐形枷锁”。如何在保证原创性的同时规避AI识别风险&#xff0c;已成为每一位研究者必须面对的现实挑…

作者头像 李华
网站建设 2026/6/6 10:54:12

新手零压力:用快马AI生成multisim14.0安装步骤模拟学习器

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 请开发一个面向新手的multisim14.0安装模拟器应用。应用核心功能包括&#xff1a;一个模拟安装程序界面的主区域&#xff0c;展示与真实安装程序类似的‘下一步’按钮、许可协议勾…

作者头像 李华
网站建设 2026/6/6 10:49:15

大众点评数据采集实战指南:五分钟破解反爬难题的完整方案

大众点评数据采集实战指南&#xff1a;五分钟破解反爬难题的完整方案 【免费下载链接】dianping_spider 大众点评爬虫&#xff08;全站可爬&#xff0c;解决动态字体加密&#xff0c;非OCR&#xff09;。持续更新 项目地址: https://gitcode.com/gh_mirrors/di/dianping_spid…

作者头像 李华