Кинжал 2, вводящий представление модели активности в фрагмент

(используя 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 все еще имеет значение null. Есть ли что-то особенное, что мне нужно сделать здесь, чтобы избежать вызова 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. 

Похоже, что Кинжал не может найти отображение, созданное 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.android.AndroidInjecton мой фрагмент dagger.android.AndroidInjecton вместо dagger.android.AndroidSupportInjection . Все еще не уверен, как исправить.

Хорошо, поэтому я нашел ответ, и он не был рядом с тем, что я думал. Мой перевод 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 } //... } 

Это было связано с тем, что ViewModelKey в Kotlin может использовать 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> Provider<? extends ViewModel>