在 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

提问人:Carlos Rosiles 提问时间:11/15/2023 更新时间:11/15/2023 访问量:61

问:

我正在开发 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

安卓 MVVM android-jetpack-compose 匕首-刀柄

评论


答:

1赞 Jan Bína 11/15/2023 #1

模块中不需要该方法。有了这个,dagger 有两种方法可以创建实例 - 它的带注释的构造函数和该方法。老实说,我不确定哪个最终会赢,但我非常确定问题在于您的存储库有多个实例。@ProvidesAppRepositoryAppRepository@Inject@Provides

此外,安装在模块中的方法并不意味着 dagger 将只创建该类的一个实例。为此,您必须使用 注释方法本身。从文档中:@ProvidesSingletonComponent@Singleton

警告:一个常见的误解是,在模块中声明的所有绑定都将限定为安装该模块的组件。然而,事实并非如此。只有使用作用域批注批注的绑定声明才会被限定范围。

这意味着有两种可能的解决方案:

  1. 删除@Provides fun provideMainRepository()

  2. 删除 and from 和添加到@Inject@SingletonAppRepository@SingletonprovideMainRepository()

-1赞 JanItor 11/15/2023 #2

关于应用程序架构 - 您可能需要一个域层。它将允许您:

  • 避免使用大型 ViewModel 并提高可读性
  • 封装复杂的业务逻辑
  • 提高可测试性
  • 具有可由多个 ViewModel 重用的业务逻辑

评论

1赞 Jesse 11/15/2023
欢迎提供指向解决方案的链接,但请确保您的答案在没有它的情况下是有用的:在链接周围添加上下文,以便您的其他用户知道它是什么以及为什么它在那里,然后引用您链接到的页面中最相关的部分,以防目标页面不可用。只不过是链接的答案可能会被删除。