Fragment 通信
本教程共 100 篇 · 第 41 篇 · 更新于 2026-07-28 · 约 8 分钟阅读
41. Fragment 通信
本节目标:掌握 Fragment 与 Activity、Fragment 与 Fragment 之间传数据的三种方式,学会选择合适的通信方案,写出松耦合的代码。
为什么通信是个问题
Fragment 设计的原则是独立、可复用。一个 Fragment 不应该知道另一个 Fragment 的存在,更不该直接操控它。但实际开发中,Fragment 之间确实需要传数据—列表页点了某项,详情页要显示对应内容。
直接 findFragmentById 拿到对方实例再调方法?能跑,但耦合太死,Fragment 没法复用到别的地方。正确的做法是通过宿主 Activity 或共享对象来中转。
方式一:Fragment Result API
这是官方推荐的 Fragment 间通信方式,适合简单的「一次性结果传递」。
打个比方:两个 Fragment 不直接说话,通过 Activity 这个「传话筒」。A 把结果放到传话筒里,B 从传话筒里取。
发送结果
从 Fragment A 发送结果给 Fragment B:
// Fragment A 里发送
val result = Bundle().apply {
putString("selectedItem", "苹果")
}
// 第一个参数是请求 key,B 要用同样的 key 监听
parentFragmentManager.setFragmentResult("itemSelected", result)
接收结果
Fragment B 在 onCreate() 里注册监听:
class DetailFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 监听结果,key 要和发送方一致
parentFragmentManager.setFragmentResultListener(
"itemSelected",
this
) { requestKey, result ->
val item = result.getString("selectedItem")
// 更新界面
updateUI(item)
}
}
}
Note
- Fragment Result API 的好处是生命周期安全。监听器绑定到
LifecycleOwner(这里是 Fragment 自己),只有 Fragment 处于STARTED状态以上才会收到结果。而且发送时机不敏感—如果 B 还没注册,结果会暂存,等 B 注册后立即投递。
适合场景
- 列表 Fragment -> 详情 Fragment 传递选中项
- 对话框 Fragment 返回选择结果
- 任何一次性、点对点的数据传递
Tip
requestKey就像频道号,发送方和接收方必须一致。一个 key 只能有一个监听器,后注册的会覆盖前面的。
方式二:共享 ViewModel
如果两个 Fragment 需要持续共享状态(而不只是一次性传值),共享 ViewModel 是更好的选择。
打个比方:Fragment Result 像递纸条,递一次就完了;共享 ViewModel 像一块共享白板,谁都能看、谁都能改,改了所有人都能看到最新值。
实现
在 Activity 作用域创建一个 ViewModel,两个 Fragment 都从 Activity 获取同一个实例:
// 共享的 ViewModel
class SharedViewModel : ViewModel() {
private val _selectedItem = MutableStateFlow<String?>(null)
val selectedItem: StateFlow<String?> = _selectedItem
fun selectItem(item: String) {
_selectedItem.value = item
}
}
Fragment A(列表页)写入数据:
class ListFragment : Fragment() {
private val viewModel: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById<Button>(R.id.btn_apple).setOnClickListener {
viewModel.selectItem("苹果")
}
}
}
Fragment B(详情页)观察数据:
class DetailFragment : Fragment() {
private val viewModel: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycleScope.launch {
viewModel.selectedItem.collect { item ->
if (item != null) {
updateUI(item)
}
}
}
}
}
Note
by activityViewModels()是关键。它表示「从宿主 Activity 获取 ViewModel」,这样两个 Fragment 拿到的是同一个实例。如果用by viewModels(),每个 Fragment 会创建自己的实例,就不共享了。
适合场景
- 主从式界面(Master-Detail):列表和详情共享选中状态
- 多步骤表单:多个 Fragment 共享填写进度
- 需要持续观察状态变化的场景
Tip
- 共享 ViewModel 配合
StateFlow/LiveData是现代 Android 的推荐做法。数据变化自动通知所有观察者,不用手动刷新。ViewModel 还能在配置变更(如旋转屏幕)时保留数据,不会丢失。
方式三:接口回调
这是比较传统的方式,Fragment 通过接口把事件交给 Activity 处理。虽然现在有了更好的方案,但了解原理有帮助。
定义接口
class ListFragment : Fragment() {
// 定义回调接口
interface OnItemSelectedCallback {
fun onItemSelected(item: String)
}
private var callback: OnItemSelectedCallback? = null
// 在 onAttach 里获取 Activity 引用
override fun onAttach(context: Context) {
super.onAttach(context)
callback = context as? OnItemSelectedCallback
}
override fun onDetach() {
super.onDetach()
callback = null // 释放引用,防泄漏
}
fun itemClicked(item: String) {
callback?.onItemSelected(item)
}
}
Activity 实现
class MainActivity : AppCompatActivity(), ListFragment.OnItemSelectedCallback {
override fun onItemSelected(item: String) {
// Activity 拿到事件后,可以传给详情 Fragment
val detailFragment = supportFragmentManager
.findFragmentById(R.id.detail_container) as? DetailFragment
detailFragment?.updateUI(item)
}
}
Warning
- 接口回调的缺点是耦合:Fragment 依赖 Activity 实现特定接口,Activity 又要直接操作详情 Fragment。三个组件互相知道对方的存在,复用性差。新项目优先用前两种方式。
三种方式对比
| 特性 | Fragment Result API | 共享 ViewModel | 接口回调 |
|---|---|---|---|
| 数据类型 | 一次性结果(Bundle) | 持续状态(Flow/LiveData) | 任意 |
| 耦合度 | 低 | 低 | 较高 |
| 生命周期安全 | 是 | 是 | 需手动处理 |
| 配置变更保留 | 否(一次性) | 是 | 否 |
| 学习成本 | 低 | 中 | 低 |
| 推荐度 | 简单传值首选 | 持续状态首选 | 了解即可 |
一些原则
-
Fragment 之间不要直接引用。别在 Fragment A 里
findFragmentById拿 Fragment B 实例然后调方法,这让 Fragment 变成了硬编码关系,没法复用。 -
通过宿主 Activity 中转。Fragment 只负责把自己的事件「报告」出去,至于事件传给谁、怎么处理,由 Activity 决定。
-
数据流向要单向。Fragment 向上传递事件(用 Result API 或回调),向下传递数据用 ViewModel 观察模式,避免双向纠缠。
Note
- 如果你在用 Compose,Fragment 通信的需求会大大减少—Compose 用状态提升和参数传递天然解决了这个问题。Fragment 通信主要在传统 View 体系或混合架构中需要。
小结
Fragment 通信记住三个方案:简单一次性传值用 Fragment Result API,持续共享状态用共享 ViewModel,接口回调了解原理即可。核心原则是松耦合—Fragment 不该知道兄弟 Fragment 的存在,通过宿主或共享对象间接通信。下一部分我们正式进入 Jetpack Compose 的世界。