@Binds
interface AnalyticsService {
fun analyticsMethods()
}
// Constructor-injected, because Hilt needs to know how to
// provide instances of AnalyticsServiceImpl, too.
class AnalyticsServiceImpl @Inject constructor(
...
) : AnalyticsService { ... }
@Module
@InstallIn(ActivityComponent::class)
abstract class AnalyticsModule {
@Binds
abstract fun bindAnalyticsService(
analyticsServiceImpl: AnalyticsServiceImpl
): AnalyticsService
}
인터페이스 자체로 생성자를 생성할 수 없으므로 abstract class에 Binds를 사용하여 주입이 가능하다.
Binds의 경우 AnalyticsServiceImpl 주입하는 부분의 코드를 갈아치워 오버헤드가 적음므로 Provides 대신 Binds를 사용하는게 좋다.
@Provides
@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {
@Provides
fun provideAnalyticsService(
// Potential dependencies of this type
): AnalyticsService {
return Retrofit.Builder()
.baseUrl("https://example.com")
.build()
.create(AnalyticsService::class.java)
}
}
유형을 삽입하는 경우가 Interface를 사용하는게 아닌 외부의 라이브러를 통해 객체를 가져오는 경우 직접 객체를 생성해주어야 하는데 이때 Provides를 사용하면 된다. 하지만 Provides의 경우 메소드를 따로 생성함으로 Binds에 비해 오버헤드가 발생한다.
@Qualifier
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptorOkHttpClient
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class OtherInterceptorOkHttpClient
//어노테이션 설정
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@AuthInterceptorOkHttpClient
@Provides
fun provideAuthInterceptorOkHttpClient(
authInterceptor: AuthInterceptor
): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(authInterceptor)
.build()
}
@OtherInterceptorOkHttpClient
@Provides
fun provideOtherInterceptorOkHttpClient(
otherInterceptor: OtherInterceptor
): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(otherInterceptor)
.build()
}
}
//interceptor를 가진 okhttp와 interceptor를 가지지 못한 okhttp를 Qualifier를 통해 구분해 준다.
@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {
@Provides
fun provideAnalyticsService(
@AuthInterceptorOkHttpClient okHttpClient: OkHttpClient
): AnalyticsService {
return Retrofit.Builder()
.baseUrl("https://example.com")
.client(okHttpClient)
.build()
.create(AnalyticsService::class.java)
}
}
// As a dependency of a constructor-injected class.
class ExampleServiceImpl @Inject constructor(
@AuthInterceptorOkHttpClient private val okHttpClient: OkHttpClient
) : ...
// At field injection.
@AndroidEntryPoint
class ExampleActivity: AppCompatActivity() {
@AuthInterceptorOkHttpClient
@Inject lateinit var okHttpClient: OkHttpClient
}
//구분한 Qualifier를 통하여 원하는 okhttp를 선택하여 할당해 준다.
Provides나 Binds를 통하여 객체를 주입할 때 동일한 객체의 생성에 대해서 구분하는 방법이다.
Qualifier를 통해 AuthInterceptorOkHttpClient, AuthInterceptorOkHttpClient등과 같은 어노테이션을 만들어주어 동일하지만 다른 okhttpclient를 제공해 줄 수 있다.
@Named
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Named("AuthInterceptor")
@Provides
fun provideAuthInterceptorOkHttpClient(
authInterceptor: AuthInterceptor
): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(authInterceptor)
.build()
}
@Named("OtherInterceptor")
@Provides
fun provideOtherInterceptorOkHttpClient(
otherInterceptor: OtherInterceptor
): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(otherInterceptor)
.build()
}
}
//interceptor를 가진 okhttp와 interceptor를 가지지 못한 okhttp를 Named를 통해 구분해 준다.
@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {
@Provides
fun provideAnalyticsService(
@Named("AuthInterceptor") okHttpClient: OkHttpClient
): AnalyticsService {
return Retrofit.Builder()
.baseUrl("https://example.com")
.client(okHttpClient)
.build()
.create(AnalyticsService::class.java)
}
}
// As a dependency of a constructor-injected class.
class ExampleServiceImpl @Inject constructor(
@Named("AuthInterceptor") private val okHttpClient: OkHttpClient
) : ...
// At field injection.
@AndroidEntryPoint
class ExampleActivity: AppCompatActivity() {
@Named("AuthInterceptor")
@Inject lateinit var okHttpClient: OkHttpClient
}
//구분한 Named를 통하여 원하는 okhttp를 선택하여 할당해 준다.
Named도 Qualifier와 동일한 기능을 한다. 별도의 어노테이션 선언 없이도 간단히 이용 가능하지만 수정하려면 직접 텍스트를 입려해 주어야 해서 네이밍등의 일괄적인 변경 등 수정 및 확장성이 떨어 짐으로 Qualifier를 이용하는 것이 좋다.
Lazy<T>
final class LazyCounter {
@Inject Lazy<Integer> lazy;
void print() {
System.out.println("printing...");
System.out.println(lazy.get());
System.out.println(lazy.get());
System.out.println(lazy.get());
}
}
printing...
computing...
100
100
100
호출되는 시점에서 데이터를 생성한다.
매번 같은 객체를 리턴해 준다.
캐싱된 데이터가 필요하다면 Lazy를 사용하면 된다.
final class LazyCounters {
@Inject LazyCounter counter1;
@Inject LazyCounter counter2;
void print() {
counter1.print();
counter2.print();
}
}
printing...
computing...
100
100
100
printing...
computing...
101
101
101
모든 클라이언트에게 동일한 객체를 제공하는 @Singleton과 같은 기능을 제공한다고 생각할 수 있지만
위와 같이 연속적으로 Lazy를 호출 시 서로 다른 객체를 생성하는 것을 확인할 수 있다.
Provider<T>
final class ProviderCounter {
@Inject Provider<Integer> provider;
void print() {
System.out.println("printing...");
System.out.println(provider.get());
System.out.println(provider.get());
System.out.println(provider.get());
}
}
printing...
computing...
100
computing...
101
computing...
102
Lazy<T>와 마찬가지로 호출하는 시점에서 데이터를 생성하지만
호출시점 마다 매번 새로운 객체를 생성한다.
캐싱된 데이터가 필요하지 않다면 Provier를 사용하면된다.
참고
https://dagger.dev/api/2.13/dagger/Lazy.html
https://developer.android.com/training/dependency-injection/hilt-android?hl=ko
'안드로이드' 카테고리의 다른 글
안드로이드 jetcaster 예제 분석 - Flow와 Combine을 이용한 흐름 제어 (0) | 2025.01.19 |
---|---|
MVI란? 안드로이드에서 MVI는 무엇이고 어떻게 적용할까? (0) | 2025.01.19 |
컴포즈 부수효과 (0) | 2022.07.30 |
Compose의 Stateful과 Stateless 개념 (0) | 2022.06.27 |
Sealed Class를 이용한 Retrofit 결과 처리 (0) | 2022.06.22 |