2つ（以上）のAPI先をAndroid HiltでDIするときの備忘録

Androidにて向き先が違うREST　APIを1つのアプリで投げることがあると思います。
実際DIしようとしたらちょっとハマったので、その備忘録。
HiltはDaggerから発展したのでDaggerでも同じことができるかもしれません。

# 結論
DIが競合しているので（この表現が正しいか分からないが）
＠Namedアノテーションを利用して区別するだけ。

# 例
他のライブラリ構成はRetrofit、okHttp、Gson
~~~ ApiModule.kt
@Module
@InstallIn(SingletonComponent::class)
object ApiModule {

    @Provides
    @Singleton
    fun provideApiOneRetrofit():Retrofit{
        return Retrofit.Builder()
            .baseUrl(REST_ONE_URL)
            .client(provideApiOneHttpClient())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    @Singleton
    @Provides
    fun provideApiOneHttpClient(): OkHttpClient =
        OkHttpClient.Builder()
            .connectTimeout(90, TimeUnit.SECONDS)
            .readTimeout(90, TimeUnit.SECONDS)
            .writeTimeout(90, TimeUnit.SECONDS)
            .addInterceptor(
                HttpLoggingInterceptor().apply {
                    level = HttpLoggingInterceptor.Level.BODY
                }
            )
            .build()

    @Provides
    @Singleton
    fun provideApiOneService(retrofit: Retrofit): ApiOneService = retrofit.create(ApiOneService::class.java)
}

~~~

~~~ apiOneService.kt
interface ApiOneService {

    @POST
    fun fechApiOne(@Body body:RequestBody):Response<ApiOneResponse>

}
~~~

~~~ ApiRepository.kt
class Repository @Inject constructor(
private val apiOneService: ApiOneService){

    suspend fun getResponse(body:RequestBody): ApiOneResponse{
        return apiOneService.fechApiOne(body).body()
    }
}
~~~

みたいなよくある構造があります。
これだけなら普通にビルドも通ると思います。
ここから新たなAPI先（API Two)を追加します。
さらにはカスタムしたOkhttpClientを用意してこちらには適合させたりします。
今回はリポジトリクラスでは2つともInjectして向き先によって利用クラスを分けたりしないようにします。

しかし新たなAPI先を叩くためのモジュールをそのまま作るとビルドエラーになります。
結論にあるようにどれがどれにDI（Inject）すればいいかライブラリが判断できなくなるためです。

~~~ ApiModule.kt
@Module
@InstallIn(SingletonComponent::class)
object ApiModule {
    @Provides
    @Singleton
    @Named("one")
    fun provideApiOneRetrofit():Retrofit{
        return Retrofit.Builder()
            .baseUrl(REST_ONE_URL)
            .client(provideApiOneHttpClient())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    @Singleton
    @Provides
    @Named("one")
    fun provideApiOneHttpClient(): OkHttpClient =
        OkHttpClient.Builder()
            .connectTimeout(90, TimeUnit.SECONDS)
            .readTimeout(90, TimeUnit.SECONDS)
            .writeTimeout(90, TimeUnit.SECONDS)
            .addInterceptor(
                HttpLoggingInterceptor().apply {
                    level = HttpLoggingInterceptor.Level.BODY
                }
            )
            .build()

    @Provides
    @Singleton
    @Named("one")
    fun provideApiOneService(retrofit: Retrofit): ApiOneService = retrofit.create(ApiOneService::class.java)

    @Provides
    @Singleton
    @Named("two")
    fun provideApiTwoRetrofit():Retrofit{
        return Retrofit.Builder()
            .baseUrl(REST_TWO_URL)
            .client(provideApiTwoHttpClient())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    @Singleton
    @Provides
    @Named("two")
    fun provideApiTwoHttpClient(): OkHttpClient =
        OkHttpClient.Builder()
            .connectTimeout(15, TimeUnit.SECONDS)
            .readTimeout(15, TimeUnit.SECONDS)
            .writeTimeout(15, TimeUnit.SECONDS)
            .addInterceptor(CustomInterceptor())
            .addInterceptor(
                HttpLoggingInterceptor().apply {
                    level = HttpLoggingInterceptor.Level.BODY
                }
            )
            .build()

    @Provides
    @Singleton
    @Named("two")
    fun provideApiTwoService(retrofit: Retrofit): ApiTwoService = retrofit.create(ApiTwoService::class.java)

}
~~~
ApiTwoServiceはOneと同じなので省略

~~~ApiRepository.kt
class Repository @Inject constructor(
@Named("one") private val apiOneService: ApiOneService,
@Named("two") private val apiTwoService: ApiTwoService){

    suspend fun getResponseOne(body:RequestBody): ApiOneResponse{
        return apiOneService.fechApiOne(body).body()
    }

    suspend fun getResponseTwo(body:RequestBody): ApiTwoResponse{
        return apiTwoService.fechApiTwo(body).body()
    }

}

~~~

このように名前をつけた＠NamedアノテーションをつけることによってどれがどれにInjectするべきかをHiltが判断できるようになります。

Hiltは便利で今後も利用頻度は高そうなので、同じようなことも起こりやすいと思います。
ただ、Daggarについても知っておいた方がいいかもしれません。
検索時もHiltだけでなくDaggerも併せて調べた方が良いでしょう。