提问人:Carlos Rosiles 提问时间:11/15/2023 更新时间:11/15/2023 访问量:61
在 Jetpack Compose 和 Dagger-Hilt 中,两个 ViewModel 中的一个未使用来自同一存储库的 StateFlow 数据进行更新
Issue with One of Two ViewModels Not Updating with StateFlow data from same Repository in Jetpack Compose and Dagger-Hilt
问:
我正在开发 Jetpack Compose 应用程序并使用 Dagger-Hilt 进行依赖注入。我有两个 ViewModel(MainViewModel 和 FormViewModel)从单例 AppRepository 订阅 StateFlow。两个 ViewModel 都正确初始化,但只有 MainViewModel 从存储库接收更新,而 FormViewModel 则没有。这两个实例都是在相同的可组合项和时间创建的。
注意:此应用程序是我使用 Jetpack Compose 和 MVVM 模式进行过的最广泛的应用程序。最初,我的应用程序只有一个 ViewModel,就像我在撰写项目中所做的那样,MainViewModel,它的大小已经显着增长(目前约为 800 行),并且仍在扩展。值得注意的是,此 ViewModel 的很大一部分(大约一半)专门用于处理表单。我的目标是将这些与表单相关的代码重构为单独的 ViewModel,从而创建 FormViewModel。我不确定这种方法是否符合使用 MVVM 和 Dagger-Hilt 的最佳实践。是否建议维护单个大型 ViewModel,或者是否可以将功能拆分为多个 ViewModel 以获得更好的模块化和可维护性?
==============
AppRepository(汇总):
@Singleton
class AppRepository @Inject constructor(...) {
private val _services = MutableStateFlow<List<LocalService>>(listOf())
val services: StateFlow<List<LocalService>> = _services
..
}
MainViewModel:
@HiltViewModel
class MainViewModel @Inject constructor(
private val appRepository: AppRepository,
private val application: Application
) : ViewModel() {
val services: StateFlow<List<LocalService>> = appRepository.services
init {
viewModelScope.launch {
services.collect { ... // Logs to print services.size
}
}
}
}
FormViewModel:
@HiltViewModel
class FormViewModel @Inject constructor(private val appRepository: AppRepository) : ViewModel() {
val services: StateFlow<List<LocalService>> = appRepository.services
init {
viewModelScope.launch {
services.collect { ... // Logs to print services.size
}
}
}
}
模块:
@Module
@InstallIn(SingletonComponent::class)
object Module {
...
@Provides
fun provideMainRepository(
...
): AppRepository {
return AppRepository(
...
)
}
============== 原木:
2023-11-14 11:31:16.170 15406-15406 FormViewModel com.conecta D MainViewModel init
2023-11-14 11:31:16.172 15406-15406 FormViewModel com.conecta D MainViewModel formQuestionCrossRef has changed: 0
2023-11-14 11:31:16.173 15406-15406 FormViewModel com.conecta D MainViewModel serviceFormCrossRef has changed: 0
2023-11-14 11:31:16.174 15406-15406 FormViewModel com.conecta D MainViewModel services has changed: 0
2023-11-14 11:31:16.757 15406-15406 FormViewModel com.conecta D FormViewModel init
2023-11-14 11:31:16.758 15406-15406 FormViewModel com.conecta D FormViewModel formQuestionCrossRef has changed: 0
2023-11-14 11:31:16.759 15406-15406 FormViewModel com.conecta D FormViewModel serviceFormCrossRef has changed: 0
2023-11-14 11:31:16.760 15406-15406 FormViewModel com.conecta D FormViewModel services has changed: 0
2023-11-14 11:31:20.028 15406-15406 FormViewModel com.conecta D MainViewModel services has changed: 30
2023-11-14 11:31:20.270 15406-15406 FormViewModel com.conecta D MainViewModel formQuestionCrossRef has changed: 1260
2023-11-14 11:31:20.274 15406-15406 FormViewModel com.conecta D MainViewModel serviceFormCrossRef has changed: 90
答:
模块中不需要该方法。有了这个,dagger 有两种方法可以创建实例 - 它的带注释的构造函数和该方法。老实说,我不确定哪个最终会赢,但我非常确定问题在于您的存储库有多个实例。@Provides
AppRepository
AppRepository
@Inject
@Provides
此外,安装在模块中的方法并不意味着 dagger 将只创建该类的一个实例。为此,您必须使用 注释方法本身。从文档中:@Provides
SingletonComponent
@Singleton
警告:一个常见的误解是,在模块中声明的所有绑定都将限定为安装该模块的组件。然而,事实并非如此。只有使用作用域批注批注的绑定声明才会被限定范围。
这意味着有两种可能的解决方案:
删除
@Provides fun provideMainRepository()
删除 and from 和添加到
@Inject
@Singleton
AppRepository
@Singleton
provideMainRepository()
关于应用程序架构 - 您可能需要一个域层。它将允许您:
- 避免使用大型 ViewModel 并提高可读性
- 封装复杂的业务逻辑
- 提高可测试性
- 具有可由多个 ViewModel 重用的业务逻辑
评论