学习 Android(十)Fragment的生命周期
简介
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 生命周期示例
- app- src/main- java/com/example/fragmentdemo- MainActivity- BaseFragment- FragmentA- FragmentB- res/layout- activity_main.xml- fragment_a.xml- fragment_b.xml
-
BaseFragment
abstract class BaseFragment : Fragment() {protected val TAG: String = "FragmentLifecycle"override fun onAttach(@NonNull context: Context) {super.onAttach(context)Log.d(TAG, "${this::class.java.simpleName} onAttach")}override fun onCreate(@Nullable savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)Log.d(TAG, "${this::class.java.simpleName} onCreate")}override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View? {Log.d(TAG, "${this::class.java.simpleName} onCreateView")return inflater.inflate(getLayoutId(), container, false)}override fun onViewCreated(@NonNull view: View, @Nullable savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)Log.d(TAG, "${this::class.java.simpleName} onViewCreated")}override fun onActivityCreated(@Nullable savedInstanceState: Bundle?) {super.onActivityCreated(savedInstanceState)Log.d(TAG, "${this::class.java.simpleName} onActivityCreated")}override fun onStart() {super.onStart()Log.d(TAG, "${this::class.java.simpleName} onStart")}override fun onResume() {super.onResume()Log.d(TAG, "${this::class.java.simpleName} onResume")}override fun onPause() {super.onPause()Log.d(TAG, "${this::class.java.simpleName} onPause")}override fun onStop() {super.onStop()Log.d(TAG, "${this::class.java.simpleName} onStop")}override fun onDestroyView() {super.onDestroyView()Log.d(TAG, "${this::class.java.simpleName} onDestroyView")}override fun onDestroy() {super.onDestroy()Log.d(TAG, "${this::class.java.simpleName} onDestroy")}override fun onDetach() {super.onDetach()Log.d(TAG, "${this::class.java.simpleName} onDetach")}/** 子类必须提供此方法来返回布局资源 ID */@LayoutResprotected abstract fun getLayoutId(): Int }
-
fragment_a.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout 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"tools:context=".FragmentA"><TextViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:textSize="25sp"android:textColor="@color/black"android:text="This is Fragment A" /></FrameLayout>
-
fragment_b.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout 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"tools:context=".FragmentB"><TextViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:textSize="25sp"android:textColor="@color/black"android:text="This is Fragment B" />
-
FragmentA
class FragmentA : BaseFragment() {override fun getLayoutId() = R.layout.fragment_a}
-
FragmentB
class FragmentB : BaseFragment() {override fun getLayoutId() = R.layout.fragment_b}
-
activity_main.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=".MainActivity"><!-- 切换 Fragment 的按钮 --><Buttonandroid:id="@+id/btn_switch"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="切换到 FragmentB"android:onClick="switchToFragmentB" /><!-- Fragment 容器 --><FrameLayoutandroid:id="@+id/fragment_container"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" /></LinearLayout>
-
MainActivity
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 首次添加 FragmentAsupportFragmentManager.beginTransaction().add(R.id.fragment_container, FragmentA()).commit()}fun switchToFragmentB(view: View) {supportFragmentManager.beginTransaction().replace(R.id.fragment_container, FragmentB()).commit()} }
-
初始加载
FragmentA
的生命周期如下FragmentA onAttach FragmentA onCreate FragmentA onCreateView FragmentA onViewCreated FragmentA onActivityCreated FragmentA onStart FragmentA onResume
-
切换
FragmentB
不加入返回栈的生命周期如下FragmentA onPause FragmentA onStopFragmentB onAttach FragmentB onCreate FragmentB onCreateView FragmentB onViewCreated FragmentB onActivityCreated FragmentB onStartFragmentA onDestroyView FragmentA onDestroy FragmentA onDetachFragmentB onResume
-
切换
FragmentB
加入返回栈的生命周期如下fun switchToFragmentB(view: View) {supportFragmentManager.beginTransaction().replace(R.id.fragment_container, FragmentB()).addToBackStack("fragmentB") // 可选:加入返回栈.commit() }
// 点击切换按钮 FragmentA onPause FragmentA onStop FragmentA onDestroyViewFragmentB onAttach FragmentB onCreate FragmentB onCreateView FragmentB onViewCreated FragmentB onActivityCreated FragmentB onStart FragmentB onResume// 按返回键返回 FragmentA FragmentB onPause FragmentB onStop FragmentB onDestroyView FragmentB onDestroy FragmentB onDetach// FragmentA 重新创建视图 FragmentA onCreateView FragmentA onViewCreated FragmentA onActivityCreated FragmentA onStart FragmentA onResume
-
4. 示例生命周期流程图
启动 Activity
└── 添加 FragmentA├── onAttach├── onCreate├── onCreateView├── onViewCreated├── onActivityCreated├── onStart└── onResume替换为 FragmentB(无返回栈)
├── FragmentA.onPause
├── FragmentA.onStop
├── FragmentA.onDestroyView
├── FragmentA.onDestroy
├── FragmentA.onDetach
└── FragmentB 完整生命周期替换为 FragmentB(有返回栈)
├── FragmentA.onPause
├── FragmentA.onStop
├── FragmentA.onDestroyView
└── FragmentB 完整生命周期(除 onDestroy/onDetach)按返回键
├── FragmentB.onPause
├── FragmentB.onStop
├── FragmentB.onDestroyView
├── FragmentB.onDestroy
├── FragmentB.onDetach
└── FragmentA 重建视图├── onCreateView├── onViewCreated├── onActivityCreated├── onStart└── 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 生命周期紧密关联,其核心顺序为:
onAttach() → onCreate() → onCreateView() → onViewCreated() → onStart() → onResume()▲ ▼ 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 实例 。
- addToBackStack(tag):在调用
-
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 交互,保证稳定性。
- 避免过度嵌套:深度嵌套会增加测量与布局开销,建议扁平化布局或使用