Lyofen
Lyofen

Reputation: 631

Android Dagger2 error: @javax.inject.Named("BaseUrl") java.lang.String is bound multiple times

I'm trying some things with Dagger2 but still difficult to understand..

I would like to use 2 services in 2 classes, SplashActivity and HomeActivity. Services depends on NetModule because i would like reuse retrofit and okhttpclient provides.

Here is my NetModule :

@Module
public class NetModule {
    @Provides
    Retrofit provideRetrofit(@Named("BaseUrl") String baseUrl, OkHttpClient okHttpClient) {
        return new Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }

    @Provides
    HttpLoggingInterceptor provideHttpLoggingInterceptor() {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        if (BuildConfig.DEBUG) {
            logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
        } else {
            logging.setLevel(HttpLoggingInterceptor.Level.NONE);
        }

        return logging;
    }

    @Provides
    OkHttpClient provideOkHttpClient(HttpLoggingInterceptor httpLoggingInterceptor, @Named("ConnectTimeoutMs") int connectTimeoutMs, @Named("ReadTimeoutMs") int readTimeoutMs) {
        return new OkHttpClient.Builder()
                .addInterceptor(httpLoggingInterceptor)
                .connectTimeout(connectTimeoutMs, TimeUnit.MILLISECONDS)
                .readTimeout(readTimeoutMs, TimeUnit.MILLISECONDS)
                .build();
    }
}

StaticDataModule and StatusModule, it's 2 differents API so they use NetModule :

@Module(includes = NetModule.class)
public class StaticDataModule {
    @Provides
    @Singleton
    StaticDataService provideStaticDataService(Retrofit retrofit) {
        return new RetrofitStaticDataService(retrofit);
    }

    @Provides
    @Named("BaseUrl")
    String provideBaseUrl() {
        return "http://url_static.com";
    }

    @Provides
    @Named("ConnectTimeoutMs")
    int provideConnectTimeoutMs() {
        return 5000;
    }

    @Provides
    @Named("ReadTimeoutMs")
    int provideReadTimeoutMs() {
        return 5000;
    }
}

@Module(includes = NetModule.class)
public class StatusModule {
    @Provides
    @Singleton
    StatusService provideStatusService(Retrofit retrofit) {
        return new RetrofitStatusService(retrofit);
    }

    @Provides
    @Named("BaseUrl")
    String provideBaseUrl() {
        return "http://url_status.com";
    }

    @Provides
    @Named("ConnectTimeoutMs")
    int provideConnectTimeoutMs() {
        return 5000;
    }

    @Provides
    @Named("ReadTimeoutMs")
    int provideReadTimeoutMs() {
        return 5000;
    }
}

And i build them in this component :

@Singleton
@Component(modules = {AppModule.class, StaticDataModule.class, StatusModule.class})
public interface AppComponent {
    void inject(SplashActivity splashActivity);
    void inject(HomeActivity homeActivity);
}

I obtain this error : @javax.inject.Named("BaseUrl") java.lang.String is bound multiple times. I understand my error, dagger dont know who provide between StaticData baseUrl and Status baseUrl.

I tried to make 2 components, StaticDataComponent and StatusComponent, but i can't inject both in the same activity.

I tried to extends NetModule in StaticDataModule and StatusModule, and give parameters with constructor, but multiple bounds error was on retrofit provide.

So i don't know how to reuse NetModule with differents parameters in 2 modules, if someone has an example it should really help me.

Thanks !

Upvotes: 4

Views: 1227

Answers (2)

Lyofen
Lyofen

Reputation: 631

Finally i found a simple solution, my architecture was bad. Now i need only one module, and retrofit is not provide, he is build inside StaticData and Status implementation :

@Module
public class NetModule {
    @Provides
    HttpLoggingInterceptor provideHttpLoggingInterceptor() {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        if (BuildConfig.DEBUG) {
            logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
        } else {
            logging.setLevel(HttpLoggingInterceptor.Level.NONE);
        }

        return logging;
    }

    @Provides
    OkHttpClient provideOkHttpClient(HttpLoggingInterceptor httpLoggingInterceptor) {
        return new OkHttpClient.Builder()
                .addInterceptor(httpLoggingInterceptor)
                .connectTimeout(5000, TimeUnit.MILLISECONDS)
                .readTimeout(5000, TimeUnit.MILLISECONDS)
                .build();
    }

    @Provides
    @Singleton
    StaticDataService provideStaticDataService(OkHttpClient okHttpClient) {
        return new RetrofitStaticDataService("http://url_1.com",okHttpClient);
    }

    @Provides
    @Singleton
    StatusService provideStatusService(OkHttpClient okHttpClient) {
        return new RetrofitStatusService("http://url_2.com",okHttpClient);
    }

}

@Singleton
@Component(modules = {AppModule.class, NetModule.class})
public interface AppComponent {
    void inject(SplashActivity splashActivity);
    void inject(HomeActivity homeActivity);
}

Retrofit objects are only in retrofit implementation class, so if i need change retrofit by another library i have just to change new RetrofitStaticDataService() by new AnotherStaticDataService().

Thanks for help Anton.

Upvotes: 2

Anton Shkurenko
Anton Shkurenko

Reputation: 4327

Do not include NetModule.class as dependency to your modules, just include them separately in the component.

@Module(includes = NetModule.class)
public class StaticDataModule {

to

@Module
public class StaticDataModule {

And then use like this:

@Component(modules = {
    NetModule.class, StaticDataModule.class
}) public interface FirstComponent {
  void inject(WhateverYouWantActivity activity);
}

@Component(modules = {
    NetModule.class, StatusModule.class
}) public interface FirstComponent {
  void inject(WhateverYouWantSecondActivity activity);
}

If you have to inject into the same activity, you better rework your architecture, but if you still want to do this, you can get rid of inject method, and add something like this:

@Component(modules = { /* your modules */ }) public interface YourComponent {

  Retrofit getRetrofit();

  OkHttpClient getOkHttpClient();
}

And use it accordingly:

retrofit = yourComponentBuiltByDagger.getRetrofit();

Instead of:

@Inject Retrofit retrofit;

Upvotes: 2

Related Questions