Использование @ Component.Builder с параметрами конструктора

Я пытаюсь изучить кинжал и kotlin и mvvm в одном, поэтому, пожалуйста, простите меня, если этот вопрос странный.

Если у меня есть NetworkModule, который в основном обеспечивает модификацию приложения, я думаю, что было бы неплохо передать базовый url, для которого мы хотим построить модификацию. Я могу сделать это по- старому , передав его через функцию сборки компонента App, но не могу понять, как это сделать с помощью метода @ Component.Builder . Попытка:

App.kt

DaggerAppComponent.builder() .application(this) .networkModule(BuildConfig.BASE_URL) .build() .inject(this) 

AppComponent.kt

 @Singleton @Component(modules = arrayOf( AppModule::class, NetworkModule::class, AndroidInjectionModule::class, ActivityBuilder::class)) interface AppComponent { @Component.Builder interface Builder { @BindsInstance fun application(application: Application): Builder @BindsInstance fun networkModule(baseUrl: String): Builder fun build(): AppComponent } fun inject(app: App) } 

NetworkModule.kt

 @Module class NetworkModule(baseUrl: String) { //Attempt to force a public setter for Dagger var baseUrl: String = baseUrl set @Provides @Singleton fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor { val loggingInterceptor = HttpLoggingInterceptor { message -> Timber.d(message) } loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY return loggingInterceptor } @Provides @Singleton fun provideOkHttpClient(httpLoggingInterceptor: HttpLoggingInterceptor): OkHttpClient { val httpClientBuilder = OkHttpClient.Builder() if (BuildConfig.DEBUG) httpClientBuilder.addInterceptor(httpLoggingInterceptor) return httpClientBuilder.build() } @Provides @Singleton fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { return Retrofit.Builder() .baseUrl(baseUrl) .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build() } } 

Ошибка довольно ясна, но я до сих пор не понимаю, что это значит 🙂 @Component.Builder is missing setters for required modules or components: [core.sdk.di.module.NetworkModule] . Я попытался заставить модуль создать публичный сеттер для базового url (хотя я думаю, что это не нужно).

Чтобы создать компонент, все модули должны быть поставлены. Теперь Dagger может создать любой модуль, у которого есть конструктор no-arg, так что вам не нужно его пропускать.

 @Module class NetworkModule(baseUrl: String) { /* ... */ } 

Вы объявляете модуль, у которого есть параметр в его construcot, поэтому Кинжал не может его построить. Это то, что ошибка пытается сказать вам:

@ Component.Builder не хватает сеттеров для необходимых модулей или компонентов: [core.sdk.di.module.NetworkModule]

У вас есть модуль, который нужно добавить вручную (потому что Кинжал не может его построить), но ваш @Component.Builder не имеет способа включить его.

То, что вы предоставляете, является привязкой для String качестве базового url, что не является лучшей идеей, поскольку вы можете использовать несколько объектов String в своем проекте. Вы должны использовать @Qualifier , но об этом позже.

 @Component.Builder interface Builder { @BindsInstance fun application(application: Application): Builder // binds a `String` to the component @BindsInstance fun networkModule(baseUrl: String): Builder // no method to set the NetworkModule! fun build(): AppComponent } 

Чтобы решить проблему, у вас есть 2 варианта. Вы можете создать сеттер и передать URL-адрес самому конструктору модуля, затем установить модуль на компоненте (1) или связать URL-адрес и просто уничтожить его в своем модуле, удалив параметр из конструктора (2) .


(1) Создайте модуль самостоятельно

Это несколько проще, поскольку вы просто создаете модуль самостоятельно и передаете его в компоновщик компонентов Dagger. Все, что вам нужно сделать, это заменить привязку вашей String с помощью установщика для модуля.

 @Component.Builder interface Builder { @BindsInstance fun application(application: Application): Builder // add the module manually fun networkModule(networkModule: NetworkModule): Builder fun build(): AppComponent } 

При создании компонента вам нужно добавить модуль в конструктор.

 builder .application(app) .networkModule(NetworkModule(MY_BASE_URL)) // create the module yourself .build() 

(2) Соберите URL-адрес

Чтобы связать URL-адрес с компонентом и просто использовать его в своем модуле – как вам кажется, вы должны удалить этот параметр из своего конструктора.

 @Module class NetworkModule() { /* ... */ } // no-arg constructor 

Делая так, Кинжал теперь может создать модуль, и вы избавитесь от этой ошибки. Вместо того, чтобы указывать url в конструкторе, мы хотим, чтобы он передавался в Dagger, но, как упоминалось выше, использование String не является оптимальным, так как оно буквально говорит кинжалу, что вам нужно это значение для любой String . Вы должны использовать квалификатор, чтобы добавить правильное различие, например, @Named("baseUrl") String .
Дополнительная информация о квалификаторах

 @Component.Builder interface Builder { @BindsInstance fun application(application: Application): Builder // bind a `String` named "baseUrl" to this component @BindsInstance fun baseUrl(@Named("baseUrl") baseUrl: String): Builder fun build(): AppComponent } 

Кинжал теперь знает, что есть @Named("baseUrl") String которую мы теперь также можем использовать в нашем модуле.

 @Module class NetworkModule() { // other methods omitted @Provides @Singleton fun provideRetrofit( // request a String named baseUrl from Dagger @Named("baseUrl") baseUrl: String, okHttpClient: OkHttpClient ): Retrofit { return Retrofit.Builder() .baseUrl(baseUrl) // ... .build() } 

Вот и все.

@ Component.Builder не хватает сеттеров для необходимых модулей или компонентов: [core.sdk.di.module.NetworkModule]

Ошибка очень буквальна, у компоновщика отсутствует метод для указания точного типа NetworkModule который он не знает, как построить в противном случае.

 @BindsInstance fun networkModule(baseUrl: String): Builder 

Этот фрагмент кода связывает тип String , он не влияет на создание NetworkModule . Поскольку NetworkModule не может быть введен и не имеет конструктора с нулевым аргументом, он должен быть задан с использованием типа строителя напрямую:

 fun networkModule(module: NetworkModule): Builder 

В App.kt вы затем переписываете построитель с помощью .networkModule(NetworkModule(BuildConfig.BASE_URL)) и он должен быть успешно .networkModule(NetworkModule(BuildConfig.BASE_URL)) .

Intereting Posts
Как создать экземпляр объекта с использованием значений параметров конструктора по умолчанию в Kotlin? Переменная не может назначаться в 2 Как сделать класс данных в Kotlin неизменным с java Date объектом в нем? Почему Kotlin не использует `List (…)` как фабрику для списков и аналогичную конвенцию для всех абстрактных коллекций? Kotlin и WebSockets Анимация вложенного фрагмента в фрагменте ViewPager запускается перед рендерингом ActivityTestRule.getActivity возвращает значение null в методе Before Метод, который не вызывается, без ошибок, может быть, связан с дженериками / сопутствующими объектами / наследованием / параллелизмом / вложением Импорт банкоматов в kotlin REPL Как liveata отправляет данные в действие, если какие-либо изменения Конвертировать проект Android для использования Gradle Script Kotlin Как сопоставить java.util.Optional <Something> с чем-то? в Котлине Как создать экземпляр универсального класса, передающего ссылку метода в конструкторе с kotlin Контроль Anko doAsync () во время вращения экрана создание приемлемого объекта в котлине