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 通信也可以使用接口回调,但大型项目里更推荐架构层的数据共享。