Androidにて向き先が違うREST APIを1つのアプリで投げることがあると思います。
実際DIしようとしたらちょっとハマったので、その備忘録。
HiltはDaggerから発展したのでDaggerでも同じことができるかもしれません。
結論
DIが競合しているので(この表現が正しいか分からないが)
@Namedアノテーションを利用して区別するだけ。
例
他のライブラリ構成はRetrofit、okHttp、Gson
@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)
}
interface ApiOneService {
@POST
fun fechApiOne(@Body body:RequestBody):Response<ApiOneResponse>
}
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)すればいいかライブラリが判断できなくなるためです。
@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と同じなので省略
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も併せて調べた方が良いでしょう。