跳到主要内容

Fragment 生命周期与通信

问题

Fragment 的生命周期是怎样的?Fragment 与 Activity、Fragment 之间如何通信?

答案

1. Fragment 生命周期

Fragment 与 Activity 生命周期对应

ActivityFragment(添加时)Fragment(移除时)
onCreateonAttachonCreateonCreateViewonViewCreated
onStartonStart
onResumeonResume
onPauseonPause
onStoponStop
onDestroyonDestroyViewonDestroyonDetach

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 的 onCreateViewonViewCreated 有什么区别?

答案

  • 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: viewLifecycleOwnerthis(Fragment)的区别?

答案

viewLifecycleOwnerthis(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
}
}

其他注意事项:

  1. onDestroyView 中取消网络请求和定时器
  2. 匿名内部类持有 Fragment 引用 → 使用弱引用或生命周期感知
  3. Handler 持有 Fragment 引用 → 在 onDestroyViewremoveCallbacksAndMessages(null)

Q5: commitAllowingStateLoss 什么时候用?

答案

commit()onSaveInstanceState() 之后调用会抛出 IllegalStateException(因为状态可能丢失)。

commitAllowingStateLoss() 允许状态丢失,不抛异常。适用于:

  • 不重要的 UI 更新(如广告 Fragment)
  • 后台任务完成后的 UI 更新(Activity 可能已 stop)

不推荐常规使用,因为可能导致 UI 状态不一致。最佳实践是使用 Lifecycle 确保在安全状态下 commit。

相关链接