gpt4 book ai didi

android - 在不同的 Activity 中注入(inject) ViewModelFactory

转载 作者:行者123 更新时间:2023-12-04 11:01:13 26 4
gpt4 key购买 nike

我正在使用著名的 Dagger-ViewModelFactory 模式来为所有 ViewModel 注入(inject)工厂。在所有的 Activity 中。

@ActivityScope
class ViewModelFactory @Inject constructor(
private val creators: MutableMap<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {

override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = creators[modelClass] ?: creators.entries.firstOrNull {
modelClass.isAssignableFrom(it.key)
}?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
return creator.get() as T
}
}


我遇到的问题是,当我将工厂注入(inject) Activity 时Dagger 失败,因为 ViewModels 的对象的提供者我不会使用的并不总是可以访问的。它们不是因为尚未添加包含提供程序的模块。

例如,我有一个 LogIn Activity 和一个 SignUp Activity ,这是我为它们添加子组件的方式:
    @ContributesAndroidInjector(modules = [
ViewModelModule::class,
FirebaseModule::class,
LogInModule::class,
BindLogInModule::class
])
@ActivityScope
internal abstract fun loginActivityInjector(): LoginActivity

@ContributesAndroidInjector(modules = [
ViewModelModule::class,
FirebaseModule::class,
SignUpModule::class,
BindSignUpModule::class
])
@ActivityScope
internal abstract fun signUpActivityInjector(): SignUpActivity

请注意,当我为 SignUpActivity 创建子组件时我不添加模块 LogInModule因为我不需要该模块中的绑定(bind)。
结果是我得到了错误

e: com.package.my.AppComponent.java:8: error: [Dagger/MissingBinding] com.package.my.login.domain.LogInAuthenticator cannot be provided without an @Provides-annotated method. public abstract interface AppComponent extends dagger.android.AndroidInjector { ^ A binding with matching key exists in component: com.package.my.di.ActivityInjectorsModule_LoginActivityInjector$app_prodDebug.LoginActivitySubcomponent com.package.my.login.domain.LogInAuthenticator is injected at com.package.my.login.repository.LoginRepository(logInAuthenticator) com.package.my.login.repository.LoginRepository is injected at com.package.my.login.domain.LoginUseCase(loginRepository) com.package.my.login.domain.LoginUseCase is injected at com.package.my.login.presentation.LoginViewModel(loginUseCase) com.package.my.login.presentation.LoginViewModel is injected at com.package.my.di.ViewModelModule.provideLoginViewModel(viewModel) java.util.Map,javax.inject.Provider> is injected at com.package.my.di.ViewModelFactory(creators) com.package.my.di.ViewModelFactory is injected at com.package.my.di.ViewModelModule.bindViewModelFactory$app_prodDebug(factory) androidx.lifecycle.ViewModelProvider.Factory is injected at com.package.my.login.ui.SignUpActivity.viewModelFactory com.package.my.login.ui.SignUpActivity is injected at dagger.android.AndroidInjector.inject(T) [com.package.my.di.AppComponent → com.package.my.di.ActivityInjectorsModule_SignUpActivityInjector$app_prodDebug.SignUpActivitySubcomponent]



发生这种情况是因为 LogInAuthenticatorLogInModule 提供.

这是否意味着唯一的解决方案是添加 LogInModule即使我真的不需要创建 GoogleSignInClientSignUpActivity ?

最佳答案

你已经声明了@ContributesAndroidInjector依赖的方法ViewModelModule .内部 ViewModelModule您已声明所有 ViewModels在那里,这意味着,当 Dagger 想要为 SignUpActivity 构建依赖关系树时它还需要明确提及 LoginViewModel应该 build 。发生这种情况是因为 Dagger 需要知道在 ViewModelModule 中声明的每个依赖项是如何声明的。应该 build 。

您的情况的解决方案将是在所有 @ContributesAndroidInjector 中包含所有模块声明(这是一种丑陋的方法), ,或者,移动SignUpViewModel的提供者方法至SignUpModule并且不包括 ViewModelModule对于 SignUpActivity宣言。

这是适合我的设置。

首先,我创建了一个 BaseActivityModule ,所有功能模块都应该包括在他们专用的@Module类:


@Module
abstract class BaseActivityModule {
@Binds abstract fun bindsViewModelFactory(factory: MyViewModelFactory): ViewModelProvider.Factory
}

Then, assuming we have 2 features: Foo and Bar:


@Module
abstract class ActivitiesModule {
@PerActivity @ContributesAndroidInjector(modules = [FooModule::class])
abstract fun contributesFooActivity(): FooActivity

@PerActivity @ContributesAndroidInjector(modules = [BarModule::class])
abstract fun contributesBarActivity(): BarActivity
}

The implementation class of ViewModelProvider.Factory should be scoped with @PerActivity because the same instance of ViewModelProvider.Factory should be provided each time that dependency is needed to be injected in the scope of particular activity:

private typealias ViewModelProvidersMap = Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>

@PerActivity
class MyViewModelFactory @Inject constructor(
private val creators: ViewModelProvidersMap
) : ViewModelProvider.Factory {

override fun <T : ViewModel> create(modelClass: Class<T>): T {
var viewModelProvider = creators[modelClass]

if (viewModelProvider == null) {
val entries = creators.entries
val mapEntry = entries.firstOrNull {
modelClass.isAssignableFrom(it.key)
} ?: throw IllegalArgumentException("Unknown model class $modelClass")
viewModelProvider = mapEntry.value
}

try {
@Suppress("UNCHECKED_CAST")
return viewModelProvider.get() as T
} catch (e: Throwable) {
throw IllegalArgumentException("Couldn't create ViewModel with specified class $modelClass", e)
}
}
}

在哪里 @PerActivity以这种方式声明:

@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class PerActivity
FooModuleBarModule声明如下:

@Module(includes = [BaseActivityModule::class])
abstract class FooModule {
@Binds @IntoMap @ViewModelKey(FooViewModel::class)
abstract fun bindsFooViewModel(viewModel: FooViewModel): ViewModel
}

@Module(includes = [BaseActivityModule::class])
abstract class BarModule {
@Binds @IntoMap @ViewModelKey(BarViewModel::class)
abstract fun bindsBarViewModel(viewModel: BarViewModel): ViewModel
}

然后我们包括 ActivitiesModuleAppComponent像这样:

@Singleton
@Component(modules = [
AndroidInjectionModule::class,
ActivitiesModule::class
])
interface AppComponent {
...
}

通过这种方法,我们已经移动了 ViewModelProvider.Factory向下创建一层:以前它位于最顶层 AppComponent现在每个子组件都将负责创建 ViewModelProvider.Factory .

关于android - 在不同的 Activity 中注入(inject) ViewModelFactory,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58783086/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com