Fragment 生命周期与通信
问题
Fragment 的生命周期是怎样的?Fragment 与 Activity、Fragment 之间如何通信?
答案
1. Fragment 生命周期
Fragment 与 Activity 生命周期对应
| Activity | Fragment(添加时) | Fragment(移除时) |
|---|---|---|
onCreate | onAttach → onCreate → onCreateView → onViewCreated | — |
onStart | onStart | — |
onResume | onResume | — |
onPause | — | onPause |
onStop | — | onStop |
onDestroy | — | onDestroyView → onDestroy → onDetach |
2. Fragment 事务
// 添加 Fragment
supportFragmentManager.commit {
setReorderingAllowed(true)
add(R.id.fragment_container, MyFragment())
}
// 替换 Fragment
supportFragmentManager.commit {
setReorderingAllowed(true)
replace(R.id.fragment_container, DetailFragment())
addToBackStack("detail") // 加入返回栈
}
// 带动画
supportFragmentManager.commit {
setCustomAnimations(
R.anim.slide_in, // enter
R.anim.fade_out, // exit
R.anim.fade_in, // popEnter
R.anim.slide_out // popExit
)
replace(R.id.container, fragment)
addToBackStack(null)
}
add vs replace
- add:在容器中添加 Fragment,原有 Fragment 仍在(重叠)
- replace:移除容器中所有 Fragment 后添加新的
replace = remove(旧) + add(新)。使用 addToBackStack 后,按返回键会逆向操作。
3. Fragment 通信方式
方式一:Fragment Result API(推荐)
// Fragment B —— 设置结果
setFragmentResult("requestKey", bundleOf("data" to "value"))
// Fragment A —— 监听结果
setFragmentResultListener("requestKey") { _, bundle ->
val data = bundle.getString("data")
}
方式二:共享 ViewModel(推荐)
// 共享 ViewModel —— 作用域为 Activity
class SharedViewModel : ViewModel() {
val selectedItem = MutableLiveData<Item>()
}
// Fragment A
class ListFragment : Fragment() {
private val sharedViewModel: SharedViewModel by activityViewModels()
fun onItemClick(item: Item) {
sharedViewModel.selectedItem.value = item
}
}
// Fragment B
class DetailFragment : Fragment() {
private val sharedViewModel: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
sharedViewModel.selectedItem.observe(viewLifecycleOwner) { item ->
updateUI(item)
}
}
}
方式三:接口回调(传统方式)
// 定义接口
interface OnItemSelectedListener {
fun onItemSelected(item: Item)
}
class ListFragment : Fragment() {
private var listener: OnItemSelectedListener? = null
override fun onAttach(context: Context) {
super.onAttach(context)
listener = context as? OnItemSelectedListener
}
override fun onDetach() {
super.onDetach()
listener = null // 防止内存泄漏
}
}
class HostActivity : AppCompatActivity(), OnItemSelectedListener {
override fun onItemSelected(item: Item) {
// 处理选择事件
}
}
4. ViewPager2 与 Fragment
// ViewPager2 Adapter
class MyPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
override fun getItemCount() = 3
override fun createFragment(position: Int): Fragment = when (position) {
0 -> HomeFragment()
1 -> SearchFragment()
2 -> ProfileFragment()
else -> throw IllegalStateException()
}
}
// 设置
viewPager.adapter = MyPagerAdapter(this)
// 配合 TabLayout
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = when (position) {
0 -> "首页"
1 -> "搜索"
2 -> "我的"
else -> ""
}
}.attach()
5. DialogFragment
class ConfirmDialog : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return AlertDialog.Builder(requireContext())
.setTitle("确认")
.setMessage("是否删除?")
.setPositiveButton("删除") { _, _ ->
setFragmentResult("confirm", bundleOf("result" to true))
}
.setNegativeButton("取消", null)
.create()
}
companion object {
fun show(fragmentManager: FragmentManager) {
ConfirmDialog().show(fragmentManager, "confirm_dialog")
}
}
}
为什么使用 DialogFragment 而不是 AlertDialog?
- DialogFragment 有自己的生命周期,屏幕旋转后会自动恢复
- 直接创建的 AlertDialog 在配置变更时会丢失
- DialogFragment 可以使用 Fragment Result API 传递结果
常见面试问题
Q1: Fragment 的 onCreateView 和 onViewCreated 有什么区别?
答案:
onCreateView:创建视图层级(inflate 布局)onViewCreated:视图已创建,初始化视图(设置监听器、观察数据)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View = inflater.inflate(R.layout.fragment_main, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// 在这里初始化 View、设置适配器、观察 LiveData
// View Binding 也应该在这里使用
}
最佳实践:onCreateView 只做 inflate,其他逻辑放在 onViewCreated。
Q2: Fragment 切换时为什么推荐用 replace + addToBackStack?
答案:
使用 replace + addToBackStack 时:
- 被替换的 Fragment 视图被销毁(
onDestroyView),但实例保留 - 按返回键时,视图重建(
onCreateView),状态从 ViewModel 或 SavedStateHandle 恢复 - 节省内存:不可见 Fragment 的视图被回收
而连续 add 会导致多个 Fragment 视图叠加,浪费内存。
Q3: viewLifecycleOwner 和 this(Fragment)的区别?
答案:
viewLifecycleOwner | this(Fragment) | |
|---|---|---|
| 生命周期 | 跟随 View(onCreateView → onDestroyView) | 跟随 Fragment(onAttach → onDetach) |
| 典型用途 | 观察 LiveData | 非视图相关操作 |
Fragment 的 View 可能被多次创建销毁(如 ViewPager 中),但 Fragment 实例可能一直存在。如果用 this 观察 LiveData,旧的 Observer 不会被移除,导致重复回调。
// ✅ 正确:用 viewLifecycleOwner
viewModel.data.observe(viewLifecycleOwner) { /* ... */ }
// ❌ 错误:用 this
viewModel.data.observe(this) { /* ... */ } // Fragment 返回栈恢复后会重复注册
Q4: 如何处理 Fragment 中的内存泄漏?
答案:
常见泄漏场景和解决方案:
class MyFragment : Fragment() {
// ❌ View Binding 泄漏
private var _binding: FragmentMainBinding? = null
private val binding get() = _binding!!
override fun onCreateView(...): View {
_binding = FragmentMainBinding.inflate(inflater, container, false)
return binding.root
}
// ✅ 必须在 onDestroyView 中清除引用
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
其他注意事项:
- 在
onDestroyView中取消网络请求和定时器 - 匿名内部类持有 Fragment 引用 → 使用弱引用或生命周期感知
- Handler 持有 Fragment 引用 → 在
onDestroyView中removeCallbacksAndMessages(null)
Q5: commitAllowingStateLoss 什么时候用?
答案:
commit() 在 onSaveInstanceState() 之后调用会抛出 IllegalStateException(因为状态可能丢失)。
commitAllowingStateLoss() 允许状态丢失,不抛异常。适用于:
- 不重要的 UI 更新(如广告 Fragment)
- 后台任务完成后的 UI 更新(Activity 可能已 stop)
但不推荐常规使用,因为可能导致 UI 状态不一致。最佳实践是使用 Lifecycle 确保在安全状态下 commit。