Мои характеристики программного обеспечения следующие:
Android Studio 3.4
dagger-android 2.16
У меня есть следующий класс, который передает MapboxGeocoder, который выполнит и вернет ответ.
class GeocodingImp(private val mapboxGeocoder: MapboxGeocoder) : Geocoding {
override fun getCoordinates(address: String, criteria: String): AddressCoordinate {
val response = mapboxGeocoder.execute()
return if (response.isSuccess && !response.body().features.isEmpty()) {
AddressCoordinate(
response.body().features[0].latitude,
response.body().features[0].longitude)
}
else {
AddressCoordinate(0.0, 0.0)
}
}
}
Однако MapboxGeocoder создается в модуле dagger во время компиляции. Поэтому я должен указать строку для адреса и TYPE_ADDRESS.
@Reusable
@Named("address")
@Provides
fun provideAddress(): String = "the address to get coordinates from"
@Reusable
@Provides
@Named("geocoder_criteria")
fun provideGeocoderCriteria(): String = GeocoderCriteria.TYPE_ADDRESS
@Reusable
@Provides
fun provideMapboxGeocoder(@Named("address") address: String, @Named("geocoder_criteria") geocoderCriteria: String): MapboxGeocoder =
MapboxGeocoder.Builder()
.setAccessToken("api token")
.setLocation(address)
.setType(geocoderCriteria)
.build()
@Reusable
@Provides
fun provideGeocoding(mapboxGeocoder: MapboxGeocoder): Geocoding =
GeocodingImp(mapboxGeocoder)
мой класс component:
interface TMDispatchMobileUIComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: TMDispatchMobileUIApplication): Builder
fun build(): TMDispatchMobileUIComponent
}
fun inject(application: TMDispatchMobileUIApplication)
}
В основном действии я бы использовал это так, поскольку пользователь может ввести другой адрес или изменить критерии на что-то другое. Но поскольку модуль скомпилирован, я не могу передавать им какие-либо параметры во время выполнения:
presenter.getAddressCoordinates("this should be the actual address", GeocoderCriteria.TYPE_ADDRESS)
Для инъекции в Activity я использую следующее:
AndroidInjection.inject(this)
Есть ли решение этой проблемы?
MapboxGeocoder динамически создаются во время выполнения, в этом случае кинжал не очень помогает, поскольку его цель - помочь вам построить граф объекта во время компиляции, как если бы вы вручную писали код.
Так что, на мой взгляд, вы должны создать MapboxGeocoder внутри getCoordinates().
Вы можете воссоздать весь свой компонент во время выполнения, если хотите, где вы затем передадите параметры своему модулю в качестве параметра конструктора. Что-то типа:
fun changeAddress(address: String) {
val component = DaggerAppComponent.builder() //Assign this to wherever we want to keep a handle on the component
.geoModule(GeoModule(address))
.build()
component.inject(this) //To reinject dependencies
}
И ваш модуль будет выглядеть так:
@Module
class AppModule(private val address: String) {...}
Однако этот метод может быть расточительным, если вы создаете много разных объектов в своем компоненте.
Другой подход по сравнению с уже приведенными ответами - получить «Фабрику» с помощью инъекции зависимостей кинжала, называемой GeoModelFactory, которая может создавать для вас новые экземпляры GeoModel.
Вы можете передать адрес и тип фабрике, которая создаст ваш экземпляр. Для оптимизации вы можете либо сохранить ссылки для всех разных адресов / типов, которые уже были запрошены (может привести к утечке памяти, если есть много разных, если старые не будут удалены), либо этого может быть достаточно, если вы сохраните только последний экземпляр и в других частях кода, чтобы просто попросить фабрику предоставить вам GeoModel, которая была создана последней.
Возникшую у вас проблему можно решить, используя подход «Вспомогательные инъекции».
Это означает, что вам нужен класс, который должен быть построен как с использованием зависимостей, предоставленных из существующих областей видимости, так и некоторых зависимостей от создателя экземпляра, в данном случае вашего основного действия. У Guice от Google симпатичный описание того, что это такое и зачем это нужно
К сожалению, в Dagger 2 нет этой функции из коробки. Однако Джейк Уортон работает над отдельная библиотека, который можно прикрепить к Dagger. Более того, вы можете найти более подробную информацию в его докладе на Droidcon London 2018, где он посвятил этому вопросу целый раздел: https://jakewharton.com/helping-dagger-help-you/
Можете ли вы предоставить ссылку на эту отдельную библиотеку? Невероятно, как такая фундаментальная задача, как передача параметра, может быть настолько сложной. Просто показывает, насколько расточительным является DI, направленный против паттерна, в целом.
Вот он: github.com/square/AssistedInject
Привет! Не могли бы вы взглянуть на предлагаемые решения?