Dagger 2 предоставляет ViewModel компонентную зависимость
Я использую Dagger 2 с несколькими привязками, чтобы обеспечить ViewModel
s. Вот мой код:
interface AppProvider : MainToolsProvider
interface MainToolsProvider {
fun provideAppContext(): Context
fun provideSchedulers(): SchedulersProvider
fun providerResourceManager(): ResourceManager
fun provideViewModelFactory(): ViewModelProvider.Factory
fun provideApi(): Api
}
@Singleton
@Component(modules = [ToolsModule::class, ViewModelModule::class])
interface MainToolsComponent : MainToolsProvider {
@Component.Builder
interface Builder {
fun build(): MainToolsComponent
@BindsInstance
fun appContext(appContext: Context): Builder
}
class Initializer private constructor() {
companion object {
fun init(appContext: Context): MainToolsProvider =
DaggerMainToolsComponent.builder()
.appContext(appContext)
.build()
}
}
}
@Module
class ToolsModule {
@Module
companion object {
@JvmStatic
@Provides
@Singleton
fun provideSchedulers(): SchedulersProvider = AppSchedulers()
@JvmStatic
@Provides
@Singleton
fun provideResourceManager(appContext: Context) = ResourceManager(appContext)
@JvmStatic
@Provides
@Singleton
fun provideGson() = Gson()
@JvmStatic
@Provides
@Singleton
fun provideConverterFactory(gson: Gson): Converter.Factory = GsonConverterFactory.create(gson)
@JvmStatic
@Provides
@Singleton
fun provideAdapterFactory(): CallAdapter.Factory = RxJava2CallAdapterFactory.create()
@JvmStatic
@Provides
@Singleton
fun provideHttpClient() = OkHttpClient.Builder().build()
@JvmStatic
@Provides
@Singleton
fun provideRetrofit(httpClient: OkHttpClient, adapterFactory: CallAdapter.Factory, converterFactory: Converter.Factory) =
Retrofit.Builder()
.baseUrl("https://api.clubinclub.com")
.addCallAdapterFactory(adapterFactory)
.addConverterFactory(converterFactory)
.client(httpClient)
.build()
@JvmStatic
@Provides
@Singleton
fun provideApi(retrofit: Retrofit) = retrofit.create(Api::class.java)
}
}
@Module
abstract class ViewModelModule {
@Binds
abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}
@Component(
dependencies = [MainToolsProvider::class]
)
@Singleton
interface AppComponent : AppProvider {
fun inject(app: ClubInClubApp)
class Initializer private constructor() {
companion object {
fun init(app: ClubInClubApp): AppComponent {
val mainToolsProvider = MainToolsComponent.Initializer.init(app)
return DaggerAppComponent.builder()
.mainToolsProvider(mainToolsProvider)
.build()
}
}
}
}
@ActivityScope
@Component(dependencies = [AppProvider::class], modules = [AuthModule::class])
interface AuthComponent {
fun inject(activity: AuthActivity)
fun inject(activity: InstagramAuthActivity)
fun viewModelFactory(): ViewModelProvider.Factory
class Initializer private constructor() {
companion object {
fun init(appProvider: AppProvider) =
DaggerAuthComponent.builder()
.appProvider(appProvider)
.build()
}
}
}
@Module
class AuthModule {
@Provides
@ActivityScope
fun provideAuthRepository(api: Api): AuthRepository = AuthRepositoryImpl(api)
@Provides
@ActivityScope
fun provideAuthInteractor(repository: AuthRepository, schedulers: SchedulersProvider): AuthInteractor = AuthInteractorImpl(repository, schedulers)
}
@FragmentScope
@Component(dependencies = [AuthComponent::class], modules = [SignUpSocialViewModelModule::class])
interface SignUpSocialComponent {
fun inject(fragment: SignUpSocialFragment)
class Initializer private constructor() {
companion object {
fun init(authComponent: AuthComponent) =
DaggerSignUpSocialComponent.builder()
.authComponent(authComponent)
.build()
}
}
}
@Module
abstract class SignUpSocialViewModelModule {
@Binds
@IntoMap
@ViewModelKey(SignUpSocialViewModel::class)
abstract fun signUpSocialViewModel(viewModel: SignUpSocialViewModel): ViewModel
}
@Singleton
class ViewModelFactory @Inject constructor(
val viewModels: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val provider = viewModels[modelClass]
if (provider != null) {
return provider.get() as T
}
throw IllegalArgumentException()
}
}
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
Как видите, я предоставляю ViewModelProvider.Factory
в ViewModelModule
который включен в MainToolsComponent
а также AppComponent
используя компонентные зависимости. Также у меня есть AuthComponent
для меня Activity
с AppProvider
зависимость и SignUpSocialComponent
с AuthComponent
зависимые пакеты. И вот мой фрагмент:
class SignUpSocialFragment : BaseBindingFragment<FragmentSignUpSocialBinding>() {
override val layoutId = R.layout.fragment_sign_up_social
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private lateinit var viewModel: SignUpSocialViewModel
private var callback: OnSocialButtonClickCallback? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(this, viewModelFactory)[SignUpSocialViewModel::class.java]
}
override fun initComponent() {
(activity as? AuthActivity)?.run {
SignUpSocialComponent.Initializer.init(component).inject(this@SignUpSocialFragment)
}
}
}
метод initComponent
называется в onCreate
метод родительского фрагмента. Проблема в том, что когда я пытаюсь скомпилировать и запустить приложение, я получаю сообщение об ошибке:
error: [Dagger/MissingBinding] [com.social.connect.clubinclub.di.base.MainToolsProvider.provideViewModelFactory()] java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.
public abstract interface MainToolsComponent extends com.social.connect.clubinclub.di.base.MainToolsProvider {
^
java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> is injected at
com.social.connect.clubinclub.presentation.base.ViewModelFactory.<init>(viewModels)
com.social.connect.clubinclub.presentation.base.ViewModelFactory is injected at
com.social.connect.clubinclub.di.app.ViewModelModule.bindViewModelFactory(factory)
android.arch.lifecycle.ViewModelProvider.Factory is provided at
com.social.connect.clubinclub.di.base.MainToolsProvider.provideViewModelFactory()
Поэтому я понятия не имею, что я делаю не так. Пожалуйста помоги
1 ответ
Удалить fun provideViewModelFactory(): ViewModelProvider.Factory
от MainToolsProvider
.
Удалить @Inject lateinit var viewModelFactory: ViewModelProvider.Factory
а также viewModel = ViewModelProviders.of(this, viewModelFactory)[SignUpSocialViewModel::class.java]
от SignUpSocialFragment
и вместо, @Inject lateinit var lateinit var viewModel: SignUpSocialViewModel
Внимательно посмотрите на ваше сообщение об ошибке:
В вашем компоненте (MainToolsProvider.provideViewModelFactory()
) Map<.., ViewModel>
не может быть предоставлено.
java.util.Map, javax.inject.Provider> не может быть предоставлен без аннотируемого метода @Provides.
Проверяя ваш код, я не вижу, как вы объявляете @Multibinds
, поэтому Кинжал будет жаловаться, так как карта пуста.
Единственное место, где вы могли бы заполнить карту, находится в SignUpSocialViewModelModule
который затем добавляется к SignUpSocialComponent
, но нам не хватает карты в MainToolsProvider
или скорее MainToolsComponent
как показано выше.
Чтобы решить ошибку компиляции вы могли бы объявить @Multibinds
так что есть хотя бы пустая карта, о которой знает Даггер, но тогда вы получите исключение во время выполнения по поводу отсутствующего провайдера для этого класса. Вы, вероятно, должны переместить @Binds @IntoMap ... ViewModel
немного в MainToolsComponent
,