首页 / Android 入门教程 / Fragment 通信

Android 入门教程

Fragment 通信

本教程共 100 篇 · 第 41 篇 · 更新于 2026-07-28 · 约 8 分钟阅读

AndroidAndroid 入门教程Fragment通信Fragment Result API共享ViewModel接口回调

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)任意
耦合度较高
生命周期安全需手动处理
配置变更保留否(一次性)
学习成本
推荐度简单传值首选持续状态首选了解即可

一些原则

  1. Fragment 之间不要直接引用。别在 Fragment A 里 findFragmentById 拿 Fragment B 实例然后调方法,这让 Fragment 变成了硬编码关系,没法复用。

  2. 通过宿主 Activity 中转。Fragment 只负责把自己的事件「报告」出去,至于事件传给谁、怎么处理,由 Activity 决定。

  3. 数据流向要单向。Fragment 向上传递事件(用 Result API 或回调),向下传递数据用 ViewModel 观察模式,避免双向纠缠。

Note
  • 如果你在用 Compose,Fragment 通信的需求会大大减少—Compose 用状态提升和参数传递天然解决了这个问题。Fragment 通信主要在传统 View 体系或混合架构中需要。

小结

Fragment 通信记住三个方案:简单一次性传值用 Fragment Result API,持续共享状态用共享 ViewModel,接口回调了解原理即可。核心原则是松耦合—Fragment 不该知道兄弟 Fragment 的存在,通过宿主或共享对象间接通信。下一部分我们正式进入 Jetpack Compose 的世界。