Fragment 通信

1. 不推荐直接互相调用

旧资料中常见写法:

val activity = getActivity() as MainActivity
activity.methodA()

或者:

val fragmentA = activity.supportFragmentManager.findFragmentByTag("A") as FragmentA
fragmentA.doSomething()

这种写法能用,但缺点明显:

  • Fragment 和 Activity 强耦合。
  • Fragment 之间互相知道对方,复用性差。
  • getActivity() 可能为 null。
  • 页面重建、返回栈、异步回调时容易出错。

现代写法更推荐以下两种:

持续共享数据:Shared ViewModel
一次性结果传递:Fragment Result API

2. Fragment 与 Activity 通信

2.1 使用接口回调

这是传统写法,适合简单事件通知。

class MenuFragment : Fragment() {

    interface Callback {
        fun onMenuSelected(id: Long)
    }

    private var callback: Callback? = null

    override fun onAttach(context: Context) {
        super.onAttach(context)
        callback = context as? Callback
            ?: error("Host Activity must implement MenuFragment.Callback")
    }

    override fun onDetach() {
        callback = null
        super.onDetach()
    }

    private fun onItemClick(id: Long) {
        callback?.onMenuSelected(id)
    }
}

Activity 实现接口:

class MainActivity : AppCompatActivity(), MenuFragment.Callback {
    override fun onMenuSelected(id: Long) {
        // 处理事件
    }
}

优点:简单直接。
缺点:Activity 必须实现接口,耦合仍然存在。

2.2 使用 Activity 级 ViewModel

适合多个 Fragment 与 Activity 共享状态。

class SharedViewModel : ViewModel() {
    private val _selectedId = MutableLiveData<Long>()
    val selectedId: LiveData<Long> = _selectedId

    fun select(id: Long) {
        _selectedId.value = id
    }
}

Fragment A 设置数据:

class ListFragment : Fragment(R.layout.fragment_list) {
    private val vm: SharedViewModel by activityViewModels()

    fun onItemClick(id: Long) {
        vm.select(id)
    }
}

Fragment B 观察数据:

class DetailFragment : Fragment(R.layout.fragment_detail) {
    private val vm: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        vm.selectedId.observe(viewLifecycleOwner) { id ->
            // 更新详情页
        }
    }
}

3. Fragment 与 Fragment 通信

3.1 共享 ViewModel

适合:

  • Tab 页面之间共享筛选条件。
  • 列表页和详情页共享当前选中项。
  • 多个 Fragment 共同维护一个页面状态。

如果是同一个 Activity 下的多个 Fragment:

private val vm: SharedViewModel by activityViewModels()

如果是父子 Fragment 之间共享:

private val vm: SharedViewModel by viewModels({ requireParentFragment() })

3.2 Fragment Result API

适合一次性结果,例如:

  • 选择城市后返回城市 ID。
  • 弹窗选择日期后返回日期。
  • 子页面完成输入后返回表单数据。

接收方:

parentFragmentManager.setFragmentResultListener(
    "request_city",
    viewLifecycleOwner
) { _, bundle ->
    val cityId = bundle.getString("city_id")
    // 处理结果
}

发送方:

parentFragmentManager.setFragmentResult(
    "request_city",
    bundleOf("city_id" to "shanghai")
)

4. 父 Fragment 与子 Fragment 通信

父 Fragment 中获取子 FragmentManager:

childFragmentManager.commit {
    replace(R.id.child_container, ChildFragment())
}

子 Fragment 获取父 Fragment:

val parent = parentFragment as? ParentFragment

但除非非常简单,否则依旧建议用:

  • 父 Fragment 作用域的 ViewModel。
  • Fragment Result API。
  • 明确的接口回调。

5. 通信方式对比

方式 适合场景 优点 缺点
直接调用 Activity 方法 简单 Demo、老项目 快 强耦合,易空指针
接口回调 Fragment 通知宿主事件 清晰 宿主必须实现接口
Shared ViewModel 持续共享状态 解耦,适合架构化 需要 ViewModel 体系
Fragment Result API 一次性返回结果 轻量,生命周期安全 只适合 Bundle 类型数据
直接 findFragment 调方法 特殊场景 直接 强耦合,不推荐

6. 面试回答模板

问题:Fragment 之间怎么通信?

可以这样回答:

老写法可以通过宿主 Activity 中转,或者通过 FragmentManager 找到另一个 Fragment 再调用方法,但这会造成强耦合。现代 Android 更推荐 Shared ViewModel 或 Fragment Result API。持续共享状态用 ViewModel,一次性返回结果用 Fragment Result API。Fragment 与 Activity 通信也可以使用接口回调,但大型项目里更推荐架构层的数据共享。