如何在运行时将参数从 activity 或片段传递给匕首模块
How to pass in parameters to a dagger module from a activity or fragment at runtime
我的软件规格如下:
Android Studio 3.4
dagger-android 2.16
我有以下 class 传递 MapboxGeocoder
将执行和 return 响应。
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
class:
interface TMDispatchMobileUIComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: TMDispatchMobileUIApplication): Builder
fun build(): TMDispatchMobileUIComponent
}
fun inject(application: TMDispatchMobileUIApplication)
}
在主要 activity 中,我会像这样使用它,因为用户可以输入不同的地址或将条件更改为其他内容。但是随着模块的编译,我无法在运行时向它们传递任何参数:
presenter.getAddressCoordinates("this should be the actual address", GeocoderCriteria.TYPE_ADDRESS)
为了注入 Activity,我使用了以下内容:
AndroidInjection.inject(this)
这个问题有什么解决办法吗?
MapboxGeocoder
是在运行时动态构建的,在这种情况下,dagger 没有多大帮助,因为它的 objective 是帮助你在编译时构建对象图,就像你手写代码。
所以在我看来,你应该在 getCoordinates()
中创建一个 MapboxGeocoder
。
如果愿意,您可以在运行时重新创建整个组件,然后将参数作为构造函数参数传递给模块。类似于:
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 的匕首依赖注入获得 "Factory",它可以为您创建新的 GeoModel 实例。
您可以将地址和类型传递给创建实例的工厂。为了优化,您可以存储已请求的所有不同 address/types 的引用(如果没有删除旧的,如果有很多不同的引用,可能会导致内存泄漏),或者如果您这样做也足够了仅存储最新的实例和代码的其他部分,只需要求工厂为您提供最后创建的 GeoModel。
您遇到的问题可以使用"Assisted injection"方法解决。
这意味着您需要使用现有范围提供的依赖项和实例创建者(在本例中为您的主要 activity)提供的依赖项来构建 class。来自 Google 的 Guice 有一个很好的 description of what it is and why it is needed
不幸的是,Dagger 2 没有开箱即用的这个功能。但是,Jake Wharton 正在研究可以附加到 Dagger 的 separate library。此外,您可以在他关于 Droidcon London 2018 的演讲中找到更多详细信息,他在演讲中专门针对这个问题做了一个完整的演讲部分:
https://jakewharton.com/helping-dagger-help-you/
我的软件规格如下:
Android Studio 3.4
dagger-android 2.16
我有以下 class 传递 MapboxGeocoder
将执行和 return 响应。
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
class:
interface TMDispatchMobileUIComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: TMDispatchMobileUIApplication): Builder
fun build(): TMDispatchMobileUIComponent
}
fun inject(application: TMDispatchMobileUIApplication)
}
在主要 activity 中,我会像这样使用它,因为用户可以输入不同的地址或将条件更改为其他内容。但是随着模块的编译,我无法在运行时向它们传递任何参数:
presenter.getAddressCoordinates("this should be the actual address", GeocoderCriteria.TYPE_ADDRESS)
为了注入 Activity,我使用了以下内容:
AndroidInjection.inject(this)
这个问题有什么解决办法吗?
MapboxGeocoder
是在运行时动态构建的,在这种情况下,dagger 没有多大帮助,因为它的 objective 是帮助你在编译时构建对象图,就像你手写代码。
所以在我看来,你应该在 getCoordinates()
中创建一个 MapboxGeocoder
。
如果愿意,您可以在运行时重新创建整个组件,然后将参数作为构造函数参数传递给模块。类似于:
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 的匕首依赖注入获得 "Factory",它可以为您创建新的 GeoModel 实例。
您可以将地址和类型传递给创建实例的工厂。为了优化,您可以存储已请求的所有不同 address/types 的引用(如果没有删除旧的,如果有很多不同的引用,可能会导致内存泄漏),或者如果您这样做也足够了仅存储最新的实例和代码的其他部分,只需要求工厂为您提供最后创建的 GeoModel。
您遇到的问题可以使用"Assisted injection"方法解决。
这意味着您需要使用现有范围提供的依赖项和实例创建者(在本例中为您的主要 activity)提供的依赖项来构建 class。来自 Google 的 Guice 有一个很好的 description of what it is and why it is needed
不幸的是,Dagger 2 没有开箱即用的这个功能。但是,Jake Wharton 正在研究可以附加到 Dagger 的 separate library。此外,您可以在他关于 Droidcon London 2018 的演讲中找到更多详细信息,他在演讲中专门针对这个问题做了一个完整的演讲部分: https://jakewharton.com/helping-dagger-help-you/