Repository中基于EventBus的事件处理:实现问题与优化咨询
一、现有实现存在的问题
内存泄漏风险:UserRepository是单例,用自定义的应用级CoroutineScope订阅全局EventBus,只要应用未被系统回收,订阅会一直存在。如果EventBus的事件持有短生命周期对象(如ViewModel、View)的引用,会导致这些对象无法被GC回收,引发内存泄漏。此外,全局单例EventBus的订阅逻辑和业务耦合过紧,难以拆分和测试。
CoroutineScope未绑定应用生命周期:当前自定义的
CoroutineScope没有和Application的生命周期关联,当应用被系统杀死时,这个Scope的Job不会自动取消,后台任务可能继续运行,浪费系统资源甚至引发崩溃。Firebase调用的线程与协程适配差:UserRepository里的Firestore调用使用了回调API,和Coroutine的配合不够顺畅,容易出现回调嵌套,且无法利用协程的取消机制处理任务中断。
FCM服务无法使用Hilt注入:
FCMNService没有添加@AndroidEntryPoint注解,无法注入Hilt提供的依赖(比如UserRepository),导致代码里的EventBus调用不仅有拼写错误(Eventbus应为EventBus),还无法直接依赖Repository完成业务逻辑。事件处理逻辑冗余:MainViewModel和UserRepository都订阅了
UserUpdated事件,会导致同一个事件被重复处理,逻辑混乱,职责划分不清晰。EventBus类型不安全:使用
MutableSharedFlow<Any>存储事件,订阅时需要手动过滤类型,容易出现类型转换错误,且事件类型无明确约束,代码可读性和维护性差。
二、优化解决方案
1. 正确管理应用级CoroutineScope
将CoroutineScope绑定到Application的生命周期,确保应用销毁时自动取消所有后台任务:
@HiltAndroidApp class MyApp : Application(), LifecycleObserver { val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) override fun onCreate() { super.onCreate() ProcessLifecycleOwner.get().lifecycle.addObserver(this) } @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onAppDestroy() { appScope.cancel() } }
然后在Hilt Module中提供这个Scope:
@Module @InstallIn(SingletonComponent::class) class MainModule { @Singleton @Provides fun provideAppScope(app: MyApp): CoroutineScope = app.appScope @Binds @Singleton abstract fun bindUserRepository(impl: UserRepositoryImpl): UserRepository }
2. 优化EventBus的类型安全与耦合度
将事件定义为密封类,避免使用Any类型,提升类型安全性:
sealed class AppEvent { sealed class UserEvent : AppEvent() { data class UserUpdated(val value: String) : UserEvent() } sealed class FCMEvent : AppEvent() { data class NewToken(val token: String) : FCMEvent() } } object EventBus { private val _events = MutableSharedFlow<AppEvent>( extraBufferCapacity = 3, onBufferOverflow = BufferOverflow.DROP_OLDEST ) val events = _events.asSharedFlow() fun publish(event: AppEvent) { _events.tryEmit(event) } suspend inline fun <reified T : AppEvent> subscribe(crossinline onEvent: (T) -> Unit) { events.filterIsInstance<T>() .collectLatest { coroutineContext.ensureActive() onEvent(it) } } }
3. 改用Firebase协程扩展API
使用Firebase的协程扩展,用suspend函数替代回调,更好地适配Coroutine:
import com.google.firebase.firestore.ktx.firestore import com.google.firebase.ktx.Firebase import kotlinx.coroutines.tasks.await class UserRepositoryImpl @Inject constructor( private val appScope: CoroutineScope ) : UserRepository { override suspend fun updateUser(value: String) { try { Firebase.firestore.collection("user").document("user1") .update("field", value) .await() // 处理成功逻辑 } catch (e: Exception) { // 处理错误逻辑 } } init { appScope.launch { EventBus.subscribe<AppEvent.UserEvent.UserUpdated> { updateUser(it.value) } } } }
4. 给FCM服务添加Hilt支持
给FCMNService添加@AndroidEntryPoint,允许注入依赖:
@AndroidEntryPoint class FCMNService : FirebaseMessagingService() { @Inject lateinit var userRepository: UserRepository override fun onNewToken(token: String) { super.onNewToken(token) EventBus.publish(AppEvent.FCMEvent.NewToken(token)) // 或者直接调用Repository方法:appScope.launch { userRepository.updateToken(token) } } override fun onMessageReceived(message: RemoteMessage) { // 处理FCM消息 } }
5. 移除冗余的事件订阅
删除MainViewModel中对UserUpdated事件的订阅,让UserRepository统一处理数据层逻辑,ViewModel只负责UI相关逻辑:
@HiltViewModel class MainViewModel @Inject constructor( private val userRepository: UserRepository ) { // 只保留UI相关逻辑,比如触发事件的方法 fun triggerUserUpdate(value: String) { EventBus.publish(AppEvent.UserEvent.UserUpdated(value)) } }
6. 考虑移除EventBus,直接依赖调用
如果业务逻辑允许,完全可以去掉EventBus,让组件直接调用UserRepository的方法,减少中间层,降低耦合:
@AndroidEntryPoint class LocationService : Service() { @Inject lateinit var userRepository: UserRepository @Inject lateinit var appScope: CoroutineScope fun onLocationChanged(location: Location) { appScope.launch { userRepository.updateUser(location.latitude.toString()) } } }
内容的提问来源于stack exchange,提问作者Tak

