17

I want to use two different backend in my android application with different response format, i am using hilt as dependency injection with retrofit for network calling, which is great to work on.

as i have added my 2nd server network files and in app module, its giving me error which is listed at the end.

i need to know a way out in this situation without making any prominent architecture changes.

@Keep
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Singleton
@Provides
fun provideRetrofit(gson: Gson,@ApplicationContext appContext: Context): Retrofit = Retrofit.Builder()
    .client(
        OkHttpClient().newBuilder()
            .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)).readTimeout(80,TimeUnit.SECONDS)
            .addInterceptor(
                ChuckerInterceptor.Builder(appContext)
                    .collector(ChuckerCollector(appContext))
                    .maxContentLength(250000L)
                    .redactHeaders(emptySet())
                    .alwaysReadResponseBody(false)
                    .build()
            )
            .build()
    )
    .baseUrl(UtilSingleton.instance!!.GetBaseUrl())
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build()

@Provides
fun provideGson(): Gson = GsonBuilder().create()

@Provides
fun providePostsService(retrofit: Retrofit): ApiService =
    retrofit.create(ApiService::class.java)

@Singleton
@Provides
fun provideApiRemoteDataSource(apiService: ApiService) = ApiRemoteDataSource(apiService)

@Singleton
@Provides
fun provideRepository(
    remoteDataSource: ApiRemoteDataSource
) =
    MainRepo(remoteDataSource)


/**----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------**/

@Singleton
@Provides
fun provideRetrofitEmall(gson: Gson,@ApplicationContext appContext: Context): Retrofit = Retrofit.Builder()
        .client(
                OkHttpClient().newBuilder()
                        .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)).readTimeout(80,TimeUnit.SECONDS)
                        .addInterceptor(
                                ChuckerInterceptor.Builder(appContext)
                                        .collector(ChuckerCollector(appContext))
                                        .maxContentLength(250000L)
                                        .redactHeaders(emptySet())
                                        .alwaysReadResponseBody(false)
                                        .build()
                        )
                        .build()
        )
        .baseUrl(UtilSingleton.instance!!.GetBaseUrl())
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build()

@Provides
fun providePostsServiceEmall(retrofit: Retrofit): EmallApiService =
        retrofit.create(EmallApiService::class.java)

@Singleton
@Provides
fun provideApiRemoteDataSource(apiService: EmallApiService) = EmallApiRemoteDataSource(apiService)

@Singleton
@Provides
fun provideRepository(
        remoteDataSource: EmallApiRemoteDataSource
) =
        EmallRepo(remoteDataSource)

}

Build Error ->

Execution failed for task ':app:kaptLocalDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution
> java.lang.reflect.InvocationTargetException (no error message)

Full Log ->

 D:\vaultsNew\vaultspaynewapis\app\build\tmp\kapt3\stubs\localDebug\com\uae\myvaultspay\di\AppModule.java:40: error: Cannot have more than one binding method with the same name in a single module
public final com.uae.myvaultspay.data.remote.ApiRemoteDataSource provideApiRemoteDataSource(@org.jetbrains.annotations.NotNull()
                                                                 ^D:\vaultsNew\vaultspaynewapis\app\build\tmp\kapt3\stubs\localDebug\com\uae\myvaultspay\di\AppModule.java:78: error: Cannot have more than one binding method with the same name in a single module
public final com.uae.myvaultspay.data.remote.emallremote.EmallApiRemoteDataSource provideApiRemoteDataSource(@org.jetbrains.annotations.NotNull()
                                                                                  ^D:\vaultsNew\vaultspaynewapis\app\build\tmp\kapt3\stubs\localDebug\com\uae\myvaultspay\di\AppModule.java:48: error: Cannot have more than one binding method with the same name in a single module
public final com.uae.myvaultspay.data.repository.MainRepo provideRepository(@org.jetbrains.annotations.NotNull()
                                                          ^D:\vaultsNew\vaultspaynewapis\app\build\tmp\kapt3\stubs\localDebug\com\uae\myvaultspay\di\AppModule.java:86: error: Cannot have more than one binding method with the same name in a single module
public final com.uae.myvaultspay.data.repository.EmallRepo provideRepository(@org.jetbrains.annotations.NotNull()
                                                           ^warning: 
File for type 'com.uae.myvaultspay.MyApplication_HiltComponents' created 
in the last round will not be subject to annotation processing.
Execution failed for task ':app:kaptLocalDebugKotlin'.
> A failure occurred while executing 
org.jetbrains.kotlin.gradle.internal.KaptExecution
> java.lang.reflect.InvocationTargetException (no error message)
Manzoor Ahmad
  • 481
  • 6
  • 21

2 Answers2

36

If you want to return Retrofit client using Dagger or Hilt, then you should use @Named annotation. This annotation helps Hilt or Dagger understand you with the same Retrofit return type, which Retrofit instance you need to get.

Following the below way, you can provide multiple instances of retrofit with hilt or dagger.

First, here I see you have provide 2 instances of retrofit. Put the @Named annotation for each instance of the retrofit you provide.

object AppModule {

   @Singleton
   @Provides
   @Named("Normal")
   fun provideRetrofit(gson: Gson, @ApplicationContext appContext: Context): Retrofit = ...

   @Singleton
   @Provides
   @Named("Email")
   fun provideRetrofitEmall(gson: Gson, @ApplicationContext appContext: Context): Retrofit = ...
}

Next, when you provide the api service, specify to Hilt or Dagger which retrofit instance it needs.

object AppModule {

   @Provides
   fun providePostsService(@Named("Normal") retrofit: Retrofit): ApiService = retrofit.create(ApiService::class.java)

   @Provides
   fun providePostsServiceEmall(@Named("Email") retrofit: Retrofit): EmallApiService = retrofit.create(EmallApiService::class.java)
}

And finally, clean the project and rebuild the project to see the results.

Dương Minh
  • 2,062
  • 1
  • 9
  • 21
  • With your log given, it's not actually the error log when you build compile. Show me your full error log. Follow this link so you can see full error log. https://stackoverflow.com/a/64830121/15369207 – Dương Minh Aug 05 '21 at 13:05
  • Oh I see. Your next problem is that you are having 2 same function provide names `provideApiRemoteDataSource()` and also `provideRepository()`. Let's rename them to different function names and rebuild. – Dương Minh Aug 05 '21 at 13:12
  • Heads down for this answer. Now this is the way to handle mutiple intances of retrofit – Benja Aug 26 '22 at 12:23
  • @benja Not only for multiple instance of Retrofit, but also you can use it for any instance. Or you can try to create a new annotations and provide it. :D It's same result with creating `@Named`. – Dương Minh Aug 26 '22 at 15:11
7

Kotlin Language

You can achieve by using Qualifier. Let's see an example:

  1. Create a kotlin class e.g. Qualifiers.kt and defined Qualifier you need

    import javax.inject.Qualifier
    
    @Qualifier
    @Retention(AnnotationRetention.BINARY)
    annotation class Auth
    
    @Qualifier
    @Retention(AnnotationRetention.BINARY)
    annotation class Setting
    
  2. Your module class e.g. NetworkModule.kt

    @Provides
    @Singleton
    @Auth   //This will differentiate retrofit object
    fun retrofitAuth(
       client: OkHttpClient,
       gsonConverterFactory: GsonConverterFactory
    ): Retrofit =
         Retrofit.Builder()
             .client(client)
             .addConverterFactory(gsonConverterFactory)
             .baseUrl("https://someauth.baseurl.com").build()
    
    
    @Provides
    @Singleton
    @Setting   //This will differentiate retrofit object
    fun retrofitSetting(
       client: OkHttpClient,
       gsonConverterFactory: GsonConverterFactory
    ): Retrofit =
         Retrofit.Builder()
             .client(client)
             .addConverterFactory(gsonConverterFactory)
             .baseUrl("https://someSetting.baseurl.com").build()
    
    
     //Build Api services with respect to qualifiers
    
     @Provides
     @Singleton
     fun authApiService(@Auth retrofit: Retrofit): AuthApiService = retrofit.create(AuthApiService::class.java)
    
    
     @Provides
     @Singleton
     fun settingApiService(@Setting retrofit: Retrofit): SettingApiService = retrofit.create(SettingApiService::class.java)
    
  3. You Api Service classes e.g. AuthApiService and SettingApiService

     interface AuthApiService {
    
          @FormUrlEncoded
          @POST("login/v2")
          fun login(@FieldMap params: HashMap<String, Any>): Response<LoginResponse>
     }
    
    
     interface SettingApiService {
    
          @GET("settings")
          fun settings(): Response<SettingsResponse>
    
          @GET("faqs")
          fun getFAQs(): Response<FAQsResponse>
    
     }
    

PS: In some cases you also need different okhttp client. In such cases, different Okhttp clients can be achieved by Qualifier as well.

Amir Raza
  • 2,320
  • 1
  • 23
  • 32