Dagger 2将活动视图模型注入片段

问题描述 投票:1回答:1

(使用kotlin)我有一个使用2个片段的设置活动的应用程序。我希望两者都获得与活动相同的SettingsViewModel实例。我假设有一个我缺少的范围问题。

首先,我有标准的ViewModelModule

@Module
abstract class ViewModelModule {
    @Binds @IntoMap
    @ViewModelKey(SettingsViewModel::class)
    abstract fun bindSettingsViewModel(viewModel: SettingsViewModel): ViewModel

    @Binds
    abstract fun bindViewModelFactory(factory: MyViewModelFactory): ViewModelProvider.Factory
}

我将我的活动绑定在:

@Module
abstract class AndroidBindingModule {
    @PerActivity
    @ContributesAndroidInjector(modules = [SettingsActivityModule::class])
    abstract fun contributeSettingsActivity(): SettingsActivity
}

有了所有其他的东西,这很好用,SettingsActivity确实得到了SettingsViewModel的一个实例。 SettingsActivityModule补充以下内容:

@PerFragment
@ContributesAndroidInjector
abstract fun contributeMainSettingsFragment(): MainSettingsFragment

@PerFragment
@ContributesAndroidInjector
abstract fun contributeDebugSettingsFragment(): DebugSettingsFragment

两个片段似乎都有调用它们的注入器(我已通过调试器检查并调用了AndroidSupportInjection.inject(fragment))。片段包括:

@Inject lateinit var mainViewModel: SettingsViewModel

但在我的片段的onCreate()中,我发现mainViewModel仍然是空的。我需要做些什么特别的事情来避免调用ViewModelProviders.of(activity)[SettingsViewModel::class.java]而是注入视图模型?

更新:

经过一番阅读后,我发现在片段中使用视图模型注入的正确方法是注入工厂并获取onActivityCreated中的视图模型:

@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
lateinit var mainViewModel: SettingsViewModel

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    mainViewModel = ViewModelProviders.of(activity, viewModelFactory)[SettingsViewModel::class.java]
}

这是有道理的,因为我有MyViewModelFactory绑定为ViewModelProvider.Factory,它用@Singleton注释。当我尝试编译上面的内容时,我收到以下错误:

Error:(6, 1) error: [dagger.android.AndroidInjector.inject(T)] java.util.Map<kotlin.reflect.KClass<? extends android.arch.lifecycle.ViewModel>,? extends javax.inject.Provider<android.arch.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.

看来Dagger无法找到ViewModelModule创建的映射。我仍然不知道如何做到这一点。也许我的树不正确?为什么AndroidBindingModule中的活动能够获取ViewModel而不是片段?

AppComponent
  - AndroidInjectionModule
  - AndroidBindingModule
  - AppModule
    - SdkModule
    - ViewModelModule
    - GotItCardModule
    - ViewHolderSubcomponent (provides a mapping of layout ID -> ViewHolder for a factory)

UPDATE

我已经做了一些深入研究...从完整的错误:

e: /home/user/workspace/Example/sdktest/build/tmp/kapt3/stubs/debug/com/example/sdktest/di/AppComponent.java:6: error: [dagger.android.AndroidInjector.inject(T)] java.util.Map<kotlin.reflect.KClass<? extends android.arch.lifecycle.ViewModel>,? extends javax.inject.Provider<android.arch.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.
e: 

e: public abstract interface AppComponent {
e:                 ^
e:       java.util.Map<kotlin.reflect.KClass<? extends android.arch.lifecycle.ViewModel>,? extends javax.inject.Provider<android.arch.lifecycle.ViewModel>> is injected at
e:           com.example.sdktest.di.viewmodel.ExampleViewModelFactory.<init>(creators)
e:       com.example.sdktest.di.viewmodel.ExampleViewModelFactory is injected at
e:           com.example.sdktest.di.viewmodel.ViewModelModule.bindViewModelFactory(factory)
e:       android.arch.lifecycle.ViewModelProvider.Factory is injected at
e:           com.example.sdktest.ui.settings.fragment.MainSettingsFragment.viewModelFactory
e:       com.example.sdktest.ui.settings.fragment.MainSettingsFragment is injected at
e:           dagger.android.AndroidInjector.inject(arg0)

我认为问题在于,不知何故Dagger试图用dagger.android.AndroidInjecton而不是dagger.android.AndroidSupportInjection注入我的片段。仍然不确定如何解决。

android kotlin dagger-2
1个回答
2
投票

好的,所以我找到了答案,并没有接近我的想法。我将GithubViewModelFactory翻译成Kotlin包括以下构造函数:

@Singleton
class MetaverseViewModelFactory @Inject constructor(
        creators: Map<KClass<out ViewModel>, Provider<ViewModel>>
): ViewModelProvider.Factory {
    private val creators: Map<Class<out ViewModel>, Provider<ViewModel>> =
            creators.mapKeys { it.key.java }
    //...
}

这是因为Kotlin的ViewModelKey只能使用KClass而不是Class。事实证明,kapt负责这一点,正确的工厂看起来像:

@Singleton
class MetaverseViewModelFactory @Inject constructor(
        private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
): ViewModelProvider.Factory {
    //...
}

注意额外的@JvmSupressWildcards也避免将Provider<ViewModel>变成Provider<? extends ViewModel>

© www.soinside.com 2019 - 2024. All rights reserved.