You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在两个不同Fragment中使用同一个Repository返回的联系人数据

实现方案

首先明确结论:不需要使用Broadcast,RxAndroid可以用但非必需,完全可以根据你现有技术栈选择更轻量化的实现,核心逻辑是让RepositoryA对外暴露无Lifecycle依赖的可观察通知通道即可。

方案1:无额外依赖,原生回调+缓存实现(最轻量化)

不需要引入任何第三方库,自己维护监听回调列表就能实现:

  1. 改造RepositoryA,内部缓存数据+维护监听列表
class RepositoryA {
    // 缓存加载完成的联系人数据
    private var cachedContactData: List<Contact>? = null
    // 用弱引用存监听回调,避免内存泄漏
    private val contactListeners = Collections.synchronizedList(
        mutableListOf<WeakReference<(List<Contact>?) -> Unit>>()
    )

    // 原有静默加载方法逻辑不变
    fun getContactData() {
        // 原有异步执行逻辑,比如协程/线程池执行
        CoroutineScope(Dispatchers.IO).launch {
            val data = // 实际从本地/接口拉取联系人的逻辑
            cachedContactData = data
            // 加载完成后通知所有监听者
            notifyAllListeners(data)
        }
    }

    // 对外暴露注册监听的方法
    fun registerContactLoadListener(listener: (List<Contact>?) -> Unit) {
        // 如果已经加载完数据,直接返回缓存结果
        cachedContactData?.let {
            listener(it)
            return
        }
        contactListeners.add(WeakReference(listener))
    }

    // 对外暴露注销监听的方法
    fun unregisterContactLoadListener(listener: (List<Contact>?) -> Unit) {
        contactListeners.removeAll { it.get() == listener }
    }

    private fun notifyAllListeners(data: List<Contact>?) {
        val validListeners = contactListeners.mapNotNull { it.get() }
        validListeners.forEach { it(data) }
        // 业务如果是一次性加载,通知完可以清空监听列表减少资源占用
        // contactListeners.clear()
    }
}
  1. FragmentB侧逻辑
    进入页面先显示加载状态,注册监听,拿到结果后更新自己的ViewModel和UI,页面销毁时注销监听避免内存泄漏:
class FragmentB : Fragment() {
    private lateinit var viewModel: FragmentBViewModel
    // 定义数据加载回调
    private val contactLoadListener: (List<Contact>?) -> Unit = { contactData ->
        viewModel.updateContactData(contactData)
        // 隐藏加载进度,刷新UI
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 进入页面先显示加载进度
        showLoadingUI()
        // 注册监听
        RepositoryA.getInstance().registerContactLoadListener(contactLoadListener)
    }

    override fun onDestroyView() {
        super.onDestroyView()
        // 页面销毁时注销监听,避免内存泄漏
        RepositoryA.getInstance().unregisterContactLoadListener(contactLoadListener)
    }
}

方案2:RxAndroid实现(适合项目已集成Rx技术栈的场景)

如果你的项目本身已经在用RxJava,不用自己维护回调列表,实现更简洁:

  1. 改造RepositoryA
class RepositoryA {
    // 用BehaviorSubject缓存结果,订阅时会直接发送最新结果
    private val contactSubject = BehaviorSubject.create<List<Contact>>()

    fun getContactData() {
        CoroutineScope(Dispatchers.IO).launch {
            val data = // 拉取数据逻辑
            contactSubject.onNext(data)
        }
    }

    // 对外暴露Observable,不对外暴露Subject避免外部篡改数据
    fun observeContactData(): Observable<List<Contact>> = contactSubject.hide()
}
  1. FragmentB侧逻辑
class FragmentB : Fragment() {
    private lateinit var viewModel: FragmentBViewModel
    private var contactDisposable: Disposable? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        showLoadingUI()
        contactDisposable = RepositoryA.getInstance().observeContactData()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { contactData ->
                viewModel.updateContactData(contactData)
                // 隐藏加载刷新UI
            }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        // 销毁时切断订阅避免内存泄漏
        contactDisposable?.dispose()
    }
}

不推荐使用Broadcast的原因

Broadcast属于系统级跨进程通信组件,用在同应用内页面通信太重,不仅需要处理动态注册/注销、权限适配,数据传递还需要序列化,性能和开发成本都远高于上述两种方案,完全没必要。

注意事项
  • 如果业务允许数据复用,可以保留RepositoryA的缓存,用户来回跳转页面不需要重新加载
  • 所有监听/订阅必须在页面销毁时注销,避免内存泄漏

内容的提问来源于stack exchange,提问作者Yogesh Byndoor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 01:24:04