LoginSignup
4
1

More than 1 year has passed since last update.

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

Posted at

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も併せて調べた方が良いでしょう。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1