匕首2将活动视图模型注入片段
(使用kotlin)我有一个应用程序使用2个片段的设置活动。 我希望两者都能像Activity一样获得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
也得到了一个SettingsActivity
的实例。 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
绑定和它用MyViewModelFactory
注释。 当我尝试编译上述我得到以下错误:
Error:(6, 1) error: [dagger.android.AndroidInjector.inject(T)] java.util.Map<kotlin.reflect.KClass,? extends javax.inject.Provider> 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 javax.inject.Provider> cannot be provided without an @Provides-annotated method. e: e: public abstract interface AppComponent { e: ^ e: java.util.Map<kotlin.reflect.KClass,? extends javax.inject.Provider> is injected at e: com.example.sdktest.di.viewmodel.ExampleViewModelFactory.(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
。 仍然不知道如何解决。
好的,所以我find了答案,并没有接近我的想法。 我把GithubViewModelFactory翻译成Kotlin包含下面的构造函数:
@Singleton class MetaverseViewModelFactory @Inject constructor( creators: Map, Provider> ): ViewModelProvider.Factory { private val creators: Map, Provider> = creators.mapKeys { it.key.java } //... }
这是由于Kotlin中的ViewModelKey
只能使用KClass
而不是Class
。 事实certificate,kapt照顾这一点,正确的工厂应该是这样的:
@Singleton class MetaverseViewModelFactory @Inject constructor( private val creators: Map, @JvmSuppressWildcards Provider> ): ViewModelProvider.Factory { //... }
请注意额外的@JvmSupressWildcards
以避免将Provider
变成Provider extends ViewModel>
Provider extends ViewModel>