【问题标题】:Dagger 2 injecting view model of activity into fragmentDagger 2将活动的视图模型注入片段
【发布时间】:2019-07-24 12:35:27
【问题描述】:

(使用 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] 而是注入视图模型,我需要在这里做些什么特别的事情吗?

更新:

看了一点点后发现,在fragment中使用视图模型注入的正确方法是注入工厂,在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 中的Activity 可以获取ViewModel 而不能获取Fragment?

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

更新

我已经对此进行了更多的挖掘......来自完整的错误:

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】:

    好的,所以我找到了答案,但它与我的想法并不接近。我将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&lt;ViewModel&gt; 变成Provider&lt;? extends ViewModel&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-26
      • 2019-02-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多