如何在两个不同Fragment中使用同一个Repository返回的联系人数据
实现方案
首先明确结论:不需要使用Broadcast,RxAndroid可以用但非必需,完全可以根据你现有技术栈选择更轻量化的实现,核心逻辑是让RepositoryA对外暴露无Lifecycle依赖的可观察通知通道即可。
方案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() } }
- 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,不用自己维护回调列表,实现更简洁:
- 改造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() }
- 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
相关产品推荐
相关产品推荐

