导读
作为 Android 四大组件之一的 Fragment,其使用频率是相当高的,但是想要管理好 Fragment 栈也不是一件容易的事。下面我们就来看看 Fragment、FragmentManager、事务、操作、回退栈、容器之间的关系。
基本概念
Fragment
什么是Fragment?
- Fragment 是 Android 在 Activity 内部的一个"子模块" ,也被称为 片段。
- 它既有 自己的布局(UI) ,也有 自己的生命周期,但必须由 activity 或其他 fragment 托管。
- 可以把它理解为:Activity 是一个大容器,Fragment 是可以复用的小页面组件。

为什么要用 Fragment?
- 模块化:可以把一个复杂界面拆分成多个小的可复用模块。
- 适配性:在手机、平板等不同屏幕上,可以灵活组合 Fragment。
- 可复用性:同一个 Fragment 可以在不同的 Activity 中复用。
- 导航管理:通过 Fragment 栈,可以方便地实现页面切换和返回。
Fragment 的生命周期
和 Activity 类似,但更细化:
- onAttach → onCreate → onCreateView → onActivityCreated → onStart → onResume
- onPause → onStop → onDestroyView → onDestroy → onDetach
创建Fragment
因为Fragment要寄托在Activity中,所以先建一个Activity:
点击包名→鼠标右键→New
→Activity
→Empty Views Activity

如果一开始没有Activity,记得勾选为Launcher Activity
,不然运行会看不到应用。

然后新建Fragment,包名右键→New
→Fragment
→Fragment(Blank)

随便取个名字,这里取为 Fragment1
:

同步会生成一个Fragment_1.xml
的布局,activity_main.xml
是刚刚新建Activity产生的:

现在要把 Fragment1
放到 MainActivity
中:
- 修改
activity_main.xml
xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!-- 加入容器 -->
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
- 修改
MainActivity.kt
kotlin
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 添加Fragment1到容器中
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, Fragment1())
.commit()
}
}
}
这样,Activity 中就能显示 Fragment1
的内容了。:

容器(FragmentContainerView)
容器就是activity_main.xml
里面的FragmentContainerView
。
- FragmentContainerView 是 Android 官方在 Fragment 1.2.0 中引入的一个容器控件。
- 它的作用是:专门用来作为 Fragment 的容器 ,推荐替代过去常用的
FrameLayout
。
为什么要有 FragmentContainerView
在以前,大家常用 FrameLayout 作为 Fragment 的占位容器:
xml
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
这种方式有几个问题:
- 不直观 :
FrameLayout
本身不是为 Fragment 准备的。 - 限制较多 :有些场景下(比如 XML 里直接用
<fragment>
标签),Fragment 的行为难以控制。 - FragmentTransaction 的一些特性支持不好。
为了解决这些问题,Google 推出了 FragmentContainerView
。所以现在的Fragment最好都使用FragmentContainerView
作为容器。
一句话总结
FragmentContainerView
是专门为 Fragment 提供的容器控件,推荐替代 FrameLayout
使用,能更好地支持 Fragment 的生命周期和事务管理。
宿主
Activity 或 Fragment 都可以是宿主,只要里面带有容器。
Fragment管理器(FragmentManager)
什么是FragmentManager?
- FragmentManager 是 Android 提供的 Fragment 管理器 ,用来在 Activity 或 Fragment 内部动态管理 Fragment。
- 它的职责是:添加、移除、替换、查找 Fragment ,以及 管理 Fragment 回退栈。
FragmentManager的几种常见获取方式
- 在 Activity 中获取
FragmentManager
kotlin
// 在 Activity 中
val fragmentManager = supportFragmentManager
// 如果是没有继承 AppCompatActivity 的原生 Activity,可以写:
val fragmentManager = fragmentManager
- 在 Fragment 中获取
FragmentManager
kotlin
// 获取父级 FragmentManager(通常用来操作 Activity 或父 Fragment 中的 Fragment)
val parentManager = parentFragmentManager
// 获取子 FragmentManager(用于管理当前 Fragment 内部的子 Fragment)
val childManager = childFragmentManager
- 在任意 Fragment 内部获取 Activity 的
FragmentManager
kotlin
val fragmentManager = requireActivity().supportFragmentManager
FragmentManager和Fragment的关系
为了更好的了解 Fragment
和 FragmentManager
之间的关系,我们再创建一个新的Fragment2
,放到Fragment1
里面:
- 修改
fragment_1.xml
,加入Fragment容器,顺便美化一下页面
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".Fragment1">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Fragment1 内容"
android:textSize="18sp"
android:textStyle="bold"
android:padding="16dp"
android:background="#E3F2FD"
android:gravity="center" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment2_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
页面效果如下:

- 修改
fragment_2.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#FFF3E0"
tools:context=".Fragment2">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Fragment2 内容"
android:textSize="16sp"
android:textStyle="bold"
android:padding="12dp"
android:background="#FF9800"
android:textColor="@android:color/white"
android:gravity="center" />
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="这是嵌套在Fragment1内部的Fragment2"
android:textSize="14sp"
android:padding="16dp"
android:gravity="center"
android:layout_margin="8dp"
android:background="@android:color/white"
android:elevation="2dp" />
</LinearLayout>
页面效果如下:

- 将
Fragment2
放到Fragment1
的容器中,修改Fragment1.kt
kotlin
class Fragment1 : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_1, container, false)
// 添加Fragment2到容器中
if (savedInstanceState == null) {
childFragmentManager.beginTransaction()
.replace(R.id.fragment2_container, Fragment2())
.commit()
}
return view
}
companion object {
@JvmStatic
fun newInstance(param1: String, param2: String) =
Fragment1()
}
}
最后的显示效果如下:

现在打印一下不同地方的FragmentManager
:
kotlin
// MainActivity.kt 中
Log.d(
"FragmentManager",
"MainActivity 中的 supportFragmentManager: $supportFragmentManager",
)
kotlin
// Fragment1.kt 中
Log.d(
"FragmentManager",
"Fragment1 中的 childFragmentManager: $childFragmentManager",
)
Log.d(
"FragmentManager",
"Fragment1 中的 parentFragmentManager: $parentFragmentManager",
)
Log.d(
"FragmentManager",
"Fragment1 中获取 Activity 的 Fragment: ${requireActivity().supportFragmentManager}",
)
kotlin
// Fragment2.kt 中
Log.d(
"FragmentManager",
"Fragment2 中的 parentFragmentManager: $parentFragmentManager",
)
Log.d(
"FragmentManager",
"Fragment2 中获取 Activity 的 Fragment 方式1: ${requireActivity().supportFragmentManager}",
)
Log.d(
"FragmentManager",
"Fragment2 中获取 Activity 的 Fragment 方式2: ${parentFragment?.parentFragmentManager}",
)
打印内容如下:

关系图如下

- 需要引用的相应
FragmentManager
属性取决于调用点在 fragment 层次结构中的位置,以及你尝试访问的 fragment 管理器。
FragmentManager,容器和宿主的关系

- 一个宿主只会有一个
FragmentManager
- 一个
FragmentManager
可以管理多个容器
FragmentManager
层级视图如下:
scss
Activity
└── supportFragmentManager (管理 Activity 下的所有 Fragment)
├── FragmentA
│ └── childFragmentManager (管理 FragmentA 的子 Fragment)
│ ├── SubFragmentA1
│ └── SubFragmentA2
│
└── FragmentB
└── childFragmentManager (管理 FragmentB 的子 Fragment)
└── SubFragmentB1
事务(FragmentTransaction)
什么是 FragmentTransaction
- FragmentTransaction 是由 FragmentManager 创建的,用来 执行一组 Fragment 的增删改查操作 的事务对象。
- 它保证这些操作要么一次性全部生效,要么都不执行,类似数据库的"事务"。
- 常见操作包括:
add()
、replace()
、remove()
、hide()
、show()
、addToBackStack()
等。
常见用法
kotlin
val transaction = supportFragmentManager.beginTransaction()
transaction.add(R.id.container, MyFragment(), "MyTag") // 添加
transaction.replace(R.id.container, AnotherFragment()) // 替换
transaction.addToBackStack(null) // 入回退栈
transaction.commit() // 提交事务
提交方式
事务的提交方式分好几种,各有差异:
- commit()
-
- 异步提交,放入主线程消息队列稍后执行
- 正常提交事务,必须在
onSaveInstanceState()
之前调用。 - 否则可能抛出异常(因为 Activity 状态已经保存)。
- commitAllowingStateLoss()
-
- 异步提交,放入主线程消息队列稍后执行
- 即使在
onSaveInstanceState()
之后调用也能提交,不会崩溃。 - 但可能导致 Fragment 状态丢失。
- commitNow() / commitNowAllowingStateLoss()
-
- 立即执行事务(同步),而不是排队等待主线程调度。
- 适合对结果有强依赖的场景,但一般不推荐频繁使用。
FragmentManager 与 FragmentTransaction 的关系
FragmentManager
:管理器,负责创建和调度 Fragment。FragmentTransaction
:事务,描述并执行对 Fragment 的具体操作。
👉 类比:FragmentManager 像银行,FragmentTransaction 像一笔转账操作。
常见问题
- 为什么 Fragment 操作需要事务机制?
为了保证一组 Fragment 操作的原子性和一致性,避免部分操作成功、部分失败,导致 UI 状态混乱。
- FragmentTransaction 提交时为什么可能会报 IllegalStateException?
因为事务提交时机晚于 onSaveInstanceState()
,状态已经保存,再修改会不一致。
- commitAllowingStateLoss() 在什么场景下可用?
比如页面退出时要立即提交事务,但即使丢失状态也无所谓。
操作
FragmentTransaction 的操作,就是指开发者在事务中定义的对 Fragment 的具体动作。
添加 Fragment
将 Fragment 添加到指定容器中。 kotlin
scss
supportFragmentManager.beginTransaction()
.add(R.id.container, MyFragment(), "MyTag")
.commit()
注意:如果同一个容器只做 add 操作加入多个 Fragment,会导致页面重叠
移除 Fragment
会先移除容器里的 Fragment,再添加新的。
kotlin
supportFragmentManager.beginTransaction()
.replace(R.id.container, AnotherFragment())
.commit()
替换 Fragment
会先移除容器里的 Fragment,再添加新的。
kotlin
supportFragmentManager.beginTransaction()
.replace(R.id.container, AnotherFragment())
.commit()
replace()
的源码本质上就是:
-
- 遍历容器里已有的 Fragment ,执行 r
emove()
。 - 再执行一次 add() ,把新的 Fragment 放进容器。
- 遍历容器里已有的 Fragment ,执行 r
- 所以
replace() = remove(all) + add(new)
。
隐藏 / 显示 Fragment
适合在多个 Fragment 间切换时使用(比如底部导航)。
kotlin
supportFragmentManager.beginTransaction()
.hide(myFragment)
.show(anotherFragment)
.commit()
注意:
- 如果退出后不在现实的Fragment,要使用
remove()
,不然会一直存在内存中。 - 如果事先通过
add()
或replace()
加入容器的 Fragment,直接调用show()
是不会生效的。
加入回退栈
让 Fragment 操作支持返回功能。
kotlin
supportFragmentManager.beginTransaction()
.replace(R.id.container, AnotherFragment())
.addToBackStack(null) // 按返回键可回退
.commit()
设置过渡动画
常用于切换页面时增加过渡效果。
kotlin
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.slide_in_right, // enter
R.anim.slide_out_left, // exit
R.anim.slide_in_left, // popEnter
R.anim.slide_out_right // popExit
)
.replace(R.id.container, AnotherFragment())
.addToBackStack(null)
.commit()
回退栈
什么是 Fragment 回退栈?
- 回退栈是 FragmentManager 提供的机制,用于记录 Fragment 的事务(Transaction)。
- 当调用
addToBackStack()
时,事务会被保存到回退栈里。 - 用户按 返回键 或调用
popBackStack()
时,FragmentManager 会把最近的事务回滚,恢复到之前的状态。
常见操作
- 加入回退栈
kotlin
supportFragmentManager.beginTransaction()
.replace(R.id.container, FragmentB())
.addToBackStack(null) // 把事务加入回退栈
.commit()
按返回键时,会回退到前一个 Fragment。
- 手动出栈
kotlin
supportFragmentManager.popBackStack()
- 清空整个回退栈
kotlin
supportFragmentManager.popBackStack(
null,
FragmentManager.POP_BACK_STACK_INCLUSIVE
)
注意点
- 如果没有调用
addToBackStack()
:
-
- 按返回键不会回到上一个 Fragment,而是直接退出 Activity。
- 回退栈存储的是 事务(Transaction) ,而不是单个 Fragment。
-
- 比如一个事务里
add(FragmentA)
和hide(FragmentB)
,回退时会一起撤销。
- 比如一个事务里
- 每个
FragmentManager
都有一个回退栈
-
- 每个
FragmentManager
(通常对应一个 Activity 或 Fragment)维护自己的回退栈。
- 每个
- 返回键默认只作用于 Activity 的回退栈
-
- 多个回退栈需要开发者自己协调,默认返回键只管理 Activity 的回退栈。常见做法是 优先消费子 Fragment 的回退栈,再退到 Activity 栈,最后退出。
- 不建议加入回退栈和没加入回退栈的事务混用。
-
- 比如如下这种情况,最后会出现
FragmentA
和FragmentB
重叠显示的问题。
- 比如如下这种情况,最后会出现
kotlin
// 事务1
add(FragmentA) // 显示FragmentA
commit()
// 事务2
hide(FragmentA) // 隐藏FragmentA
addToBackStack() // 加入返回栈
commit()
// 事务3
add(FragmentB) // 显示FragmentB
commit
// 回退
popBackStack() // 回退事务2
生命周期演示
现在我们来看一下执行不同的操作,Fragment的生命周期状态是怎么变化的。
新建一个Fragment3
,和Fragment1
同级,用于切换的时候查看生命周期变化。再加入两个Fragment相关的控制逻辑。
层次结构如下图:
MainActivity
├── Fragment1
│ └── Fragment2
└── Fragment3
页面内容如下:

不带返回栈的事务操作
add Fragment1


和直接调用replace触发的生命周期一样
remove Fragment1


replace Fragment1
→hide Fragment1
→show Fragment1

hide
和show
不会销毁视图,变化只能通过onHiddenChanged
方法监听


add Fragment1
->replace Fragment3


带返回栈的事务操作
修改MainActivity
,将操作都加到返回栈中。修改后页面如下:

- 执行
ADD F1
→REMOVE F1
→POP

可以看到这里和没有加入回退栈的remove
事务相比,不会执行onDestroy
方法:

这里注意,嵌套的Fragment2
会经历先销毁再创建的过程:

这里可以通过先判断Fragment2
是否存在,再通过判断要不要创建来解决:
kotlin
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.d(TAG, "onCreateView() - Fragment1 创建视图")
val view = inflater.inflate(R.layout.fragment_1, container, false)
// 检查Fragment2是否已经存在
val existingFragment2 = childFragmentManager.findFragmentById(R.id.fragment2_container)
if (existingFragment2 == null) {
childFragmentManager.beginTransaction()
.replace(R.id.fragment2_container, Fragment2.newInstance())
.commit()
}
return view
}
其他的操作就不一一演示了,大家可以在文章末尾找到对应的demo仓库地址,自己操作一遍理解会更加深刻
提示:通过logcat查看日志的时候,可以通过tag:Fragment1_Lifecycle tag:Fragment2_Lifecycle tag:Fragment3_Lifecycle tag:MainActivity_FragmentControl
过滤内容。
后记
如果自己管理 Fragment 栈,需要掌握的知识点很多,并且也容易踩坑。好处是能够精细化的控制,做性能优化。
官方推荐使用 Jetpack Navigation
路由组件,相对手动管理,用路由组件的方式更适合大型项目,能减少很多样板代码。
大家有什么好用的路由库,也可以在评论区留言交流。