(使用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)
更新
我已经做了一些深入研究......从完整的错误:
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
注入我的片段。仍然不确定如何解决。
答案 0 :(得分: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>