'How to pass in parameters to a dagger module from a activity or fragment at runtime

My software specifications are as follows:

Android Studio 3.4
dagger-android 2.16

I have the following class that passes a MapboxGeocoder that will execute and return a response.

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)
        }
    }
}

However, the MapboxGeocoder is generated in a dagger module at compile time. So I have to specify the string for the address and 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)

my component class:

interface TMDispatchMobileUIComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: TMDispatchMobileUIApplication): Builder

        fun build(): TMDispatchMobileUIComponent
    }

    fun inject(application: TMDispatchMobileUIApplication)
}

In the main activity I would use this like this as the user can enter in a different address or change the criteria to something else. But as the module are compiled I cannot pass any parameters to them at runtime:

presenter.getAddressCoordinates("this should be the actual address", GeocoderCriteria.TYPE_ADDRESS)

For my injection into the Activity I use the following:

AndroidInjection.inject(this)

Is there any solution to this problem?



Solution 1:[1]

You can recreate your whole component at runtime if you wish, where you'd then pass in the parameters to your module as a constructor parameter. Something like:

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
}

And your module would look like:

@Module
class AppModule(private val address: String) {...}

This method may be wasteful though, if you're creating many different objects in your component.

Solution 2:[2]

A different approach compared to the already given answers would be to get a "Factory" via dagger dependency injection called GeoModelFactory which can create new instances of GeoModel for you.

You can pass the address and type to the factory which creates your instance. For optimization you can either store references for all different address/types that have already been requested (can result in a memory leak if there are a lot of different ones if old ones are not removed) or it could also be enough if you store only the latest instance and in other parts of the code to simply ask the factory to provide you with the GeoModel that has been created last.

Solution 3:[3]

The MapboxGeocoder are dynamically constructed at runtime, in this case, dagger doesn't help much as its objective is to help you construct the object graph at compile time like you hand write the code.

So in my opinion, you should create a MapboxGeocoder inside getCoordinates().

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 urgentx
Solution 2 CitrusO2
Solution 3 jaychang0917