Я начал использовать Dagger2, так что еще есть чему поучиться.Мне интересно, может ли кто-нибудь указать мне правильное направление.
Итак, я создал модуль для регистрации моделей представлений, используемых в моей деятельности.Это выглядит так:
@Module
abstract class ViewModelModule {
@Binds
internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
@Binds
@IntoMap
@ViewModelKey(MainActivityViewModel::class)
internal abstract fun bindMainActivityViewModel(viewModel: MainActivityViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(ShowDetailsViewModel::class)
abstract fun bindShowDetaislViewModel(viewModel: ShowDetailsViewModel): ViewModel
}
ViewModelKey
- это простой класс вспомогательных аннотаций, который выглядит следующим образом:
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey (val value: KClass<out ViewModel>) {
}
ViewModelModule
загружается моим основным компонентом приложения (используетсядля создания приложения):
@Singleton
@Component(
modules=[
AndroidSupportInjectionModule::class,
AppModule::class,
DatabaseModule::class,
NewsServiceModule::class,
JobBindingModule::class,
ViewModelModule::class,
PreferencesModule::class,
ActivityBindingModule::class
]
)
interface AppComponent: AndroidInjector<MyApp> {
@Component.Builder
abstract class Builder: AndroidInjector.Builder<MyApp>()
}
А вот код для ActivityBindingModule
, отвечающего за настройку подкомпонентов (в данном случае, операций, используемых моим приложением):
@Module
abstract class ActivityBindingModule {
@ActivityScoped
@ContributesAndroidInjector()
internal abstract fun mainActivity(): MainActivity
@ActivityScoped
@ContributesAndroidInjector
internal abstract fun showDetailsActivity(): ShowDetailsActivity
}
Внутри каждого действия создается модель представления с помощью кода, который выглядит следующим образом (вызывается из метода onCreate
):
//view model code
_viewModel = ViewModelProviders.of(this, viewModelFactory)[ShowDetailsViewModel::class.java]
И, как и следовало ожидать, вводится viewModelFactory
as field:
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
Обе модели представлений имеют внешние зависимости, которые устанавливаются на другие модули, на которые ссылается верхний компонент приложения.
И, для полноты, вот кодна мой взгляд модель фабрики:
@Singleton
class ViewModelFactory @Inject constructor(private val viewModels: MutableMap<Class<out ViewModel>, Provider<ViewModel>>) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T
= viewModels[modelClass]?.get() as T
Этот код работает, но, похоже, его можно улучшить.После прочтения документации у меня сложилось впечатление, что я мог бы провести рефакторинг своего ViewModeModule
, чтобы он просто создавал мой ViewModelFactory
и перемещал каждое из объявлений модели представления в отдельный модуль (так, чтобы каждое из них могло быть введено тольков «правильной» деятельности).
Чтобы проверить это, я начал с перемещения ShowDetailsViewModel
в новый модуль, который имеет только одну запись:
@Module
internal abstract class DetailsModule {
@Binds
@IntoMap
@ViewModelKey(ShowDetailsViewModel::class)
abstract fun bindShowDetaislViewModel(viewModel: ShowDetailsViewModel): ViewModel
}
После этого ViewModelModule выглядит следующим образом:
@Module
abstract class ViewModelModule {
@Binds
internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
@Binds
@IntoMap
@ViewModelKey(MainActivityViewModel::class)
internal abstract fun bindMainActivityViewModel(viewModel: MainActivityViewModel): ViewModel
}
И я обновил ActivityBindingModule
, чтобы он выглядел следующим образом:
@Module
abstract class ActivityBindingModule {
@ActivityScoped
@ContributesAndroidInjector()
internal abstract fun mainActivity(): MainActivity
@ActivityScoped
@ContributesAndroidInjector(modules = [DetailsModule::class])
internal abstract fun showDetailsActivity(): ShowDetailsActivity
}
Обратите внимание, что теперь я передаю DetailsModule
(который создает экземпляр ShowDetailsViewModel
) к аннотации ContributeAndroidInjector
, которая применяется к методу showDetailsActivity
, потому что эта модель представления используется только этим действием.
Теперь я определенно что-то упускаю, потому что после этого я 'm всегда получает следующее исключение:
java.lang.IllegalStateException: ViewModelProviders.of(th…ilsViewModel::class.java] must not be null
Если я отлаживаю приложение, я вижу, что перемещение ShowDetailsViewModel в его собственную модель не регистрирует его на карте, используемой фабрикой (т. е. карта имееттолько одна запись, которая соответствует MainActivityViewModel
, зарегистрированному в ViewModelModule.
Я подумал, что перемещение каждой модели представления объявления в каждый модуль, используемый субкомпонентом, все же должно позволить ему быть зарегистрированным вмамаp вводится модулем, который зарегистрирован в верхнем компоненте.Я ошибся?Что мне не хватает, чтобы сделать эту работу?
Спасибо.