Android 뿐만 아니라 프로그래밍에서 널리 사용되는 기법으로 다양한 이점이 있다.
- 코드의 재사용성
- 리펙토링 용이성
- 테스트 용이성
Class에서 다른 Class를 참조하는 방법은 크게 3가지가 있다.
1. Class 에서 필요한 종속 Class 를 인스턴스화 하는 방법
위와 같은 방법은 Car와 Engine이 밀접하게 연결되어 있기 때문에 문제가 발생할 수 있다.
만약 Engine을 상속받은 GasEngine, ElectricEngine Class가 있을 때 쉽게 대체할 수 없다.
- Engine이라는 한 가지 유형을 사용하기 때문에 수정하면 연관된 코드를 수정해야 할 가능성이 있다.
Test를 더욱 어렵게 한다.
- Engine을 FakeEngine으로 바꿔서 Test 하려면 Engine과 연관된 코드를 수정해야 한다.
2. 좌측 getter/ setter 를 이용한 방법 (필드 삽입, setter 삽입)
3. 우측 생성자를 통해 넘겨주는 방법 (생성자 삽입)
위 방법이 DI를 이용한 프로그래밍 방법이고 1번에서 발생한 문제점을 쉽게 해결할 수 있다.
- Engine의 서브 클래스들을 쉽게 대입할 수 있기 때문에 Car를 재사용할 수 있고, 쉽게 테스트할 수 있다.
Hilt
Hilt는 Android Class에 Lifecycle을 고려한 DI(의존성 주입)을 지원하는 라이브러리이다.
기존의 Dagger라는 의존성 라이브러리를 Android의 Lifecycle에 맞게 보완하여 나온 라이브러리로 Hilt는 2,3번을 쉽게 도와주며 중복 코드와 보일러 코드를 최소화할 수 있다.
Hilt를 사용하기 위해선 ApplicationClass가 필요
Hilt라이브러리는 ApplicationClass와 ApplicationContext에 접근해서 많은 일들을 뒤에서 해주어야 하기 때문
클래스의 윗부분에 @HiltAndroidApp 이라는 Annotation을 추가해 주어야 한다.
Annotation에 의해서 Singleton Component가 생성되게 되는데 앱이 살아있는 동안
Dependency를 제공하는 역할을 하는, 애플리케이션 레벨의 Component인 것
클래스를 생성한 후 AndroidManifest파일의 application 태그에 name을 설정해주면 Hilt는 컴포넌트들을 이용해서 필요한 Dependency를 클래스에 제공할 수 있다.
Dependency 주입 종류
Hilt가 Dependency를 주입해 줄 수 있는 클래스의 종류
- Activity
- ComponentActivity를 상속받는 Activity만 지원 ex) AppCompatActivity
- Fragment
androidx.Fragment를 상속받는 Fragment클래스에만 의존성 주입 가능/Update로 인한 androidx.Fragment 기본 사용
- View
- Service
- BroadcastReceiver
위 클래스들에게 @AndroidEntryPoint를 붙여주면 Hilt가 해당 클래스에 Dependency를 제공해 줄 수 있는 컴포넌트를 생성
생성된 Component들은 아래와 같이 계층을 가짐 각 컴포넌트들은 그 부모로부터 Dependency를 받을 수 있음
Injection
어떤 곳에서 해당 Dependency가 필요하다고 Annotation이 붙어 있다면, 해당하는 객체를 어떻게 생성하는지 Hilt가 알고 있어야 한다.
Injection은 Install 된 컴포넌트로부터 Dependency를 주입하거나 받을 수 있다는 것을 의미하고 @Inject Annotation은 Dependency Graph를 이어주는 역할을 한다
Hilt가 Dependency를 제공해서 생성할 객체의 클래스에도 붙이고 Dependency를 주입받을 객체에도 붙여준다.
Injection은 Field Injection과 Constructor Injection 크게 두 가지로 나뉜다.
Field Injection
Hilt에 의해 주입받을 수 있는 객체의 클래스라는 것을 알려주기 위해 생성자에 @Inject을 붙여준다.
이 클래스는 Component 들에서 사용이 가능, 대표적으로 @AndroidEntryPoint를 붙인 Activity나 Fragment에서 사용이 가능
아래 같이 @Inject를 붙여서 원하는 field에 객체를 주입받을 수 있게 되어 해당 클래스를 생성하지 않고도 객체를 주입받아 사용하는 것
Constructor Injection
A클래스와 B클래스가 있다고 가정할 때
B클래스는 @Inject를 붙여 주입될 객체라고 표시, A클래스도 마찬가지지만 생성자에서 B클래스의 객체를 주입받는다
이것을 Constructor Injection이라고 한다.
Constructor Injection 사용함으로써 생성 시에 어떤 클래스의 객체가 필요한지 Hilt가 알 수 있고, 개발자도 알 수 있다.
위 두 클래스를 이용해서, 위와 같이 field injection으로 객체를 주입받아 사용이 가능
Injection 예외
1. Interface를 Constructor Injection에 사용하는 것은 금지
- Hilt가 interface의 implement된 타입의 객체를 어떻게 생성해야 할지 알 수 없기 때문
AInterface를 implement 하는A와 B가 있을 때, Constructor Injection을 사용하면 compile 오류 발생
Interface나 Interface를 implement 하는 객체를 Inject 할 수 없다는 것
Hilt가 Interface가 implement 된 타입의 객체를 어떻게 생성해야 할지 알 수 없기 때문
2. 외부 라이브러리 클래스의 객체를 Inject하는 것은 금지
- 자신이 만든 클래스가 아닌 곳에 @Inject를 annotation 추리를 할 수 없기 때문
- Hilt가 이 객체를 어떻게 만들어야 할지 모르기 때문
이 경우 HiltModule과 Provides와 Binds를 사용해 해당 객체에 대해 Hilt에게 원하는 Dependency 알려주어야 한다.
Provides
Module 클래스를 생성할 때 @Module Annotation을 지정 - Hilt에게 Module이 있는 곳을 알림
- @Installn Annotation을 지정해 Hilt에게 이 클래스의 객체가 어디에서 사용되는지 알림
- @Installn(SingletonComponent::class)
- 해당 모듈이 Application 단에서 사용 가능하다고 선언
- @Installn(ActivityComponent::class)
- Activity에서 사용 가능
- @Installn(SingletonComponent::class)
Module 내부에 Provides 함수들을 구현, @Provides Annotation 지정
Provide 방식을 이용하면, Retrofit이나 Gson과 같은 외부 라이브러리 객체의 경우도 Dependency를 제공 가능
Binds
Provides와는 다르게 외부 라이브러리에 사용할 수 없고 Interface타입의 객체를 어떻게 생성할지 Hilt에게 알려주기 위한 용도로 사용
Abstract class를 만든 후, abstract 함수를 정의 후 @Binds 지정 사용
HiltViewModel
ViewModel에서의 의존성 주입은 @HiltViewModel Annotation을 지정해 사용
Annotation을 지정하였다면 아래와 같이 @AndroidEntryPoint Annotation이 붙은 Activity나 Fragment에서 아래와 같이 바로 사용 가능
@Qualifier (구분자)
같은 타입의 객체에 대한 Dependency를 주입해야 할 때 타입이 여러 개가 사용될 경우 어떤 Type의 객체를 Inject 할지Hilt는 알 수가 없다.
그래서 이를 구분하기 위에 @Qualifier를 사용하여 구분
@Retention은 Annotention의 Lifecycle을 설정 - Annotention이 언제까지 살아 있을지
위 오른쪽 사진 A클래스의 경우 Ainterface타입으로 객체를 두 개 받는데 Hilt는 어떤 객체를 연결시켜야 되는지 모르기 때문에 생성자들의 인자 앞에 만든 Qualifier를 이용해 각각 다른 Indentifier(식별자)를 두어 객체를 구분
Scope
Hilt는 각 Class들에 대응하는 Component와 Scope를 같이 유지함으로써 매번 객체를 주입할 때마다 새로운 객체를 생성하는 것이 아닌, 해당 Scope내에서 사용할 수 있도록 한다.
Component에 맞지 않은 Scope를 지정할 시 에러가 나게 된다.
Scope를 지정하지 않고 사용하는 상태는 해당 Class를 계속 생성하게 된다.
Hilt
https://developer88.tistory.com/349
https://leveloper.tistory.com/205
'Android > Library' 카테고리의 다른 글
GeoCoding 위도 경도 <-> 주소 변환하기 (4) | 2022.02.20 |
---|---|
Google Map API 사용 방법 및 예제 (0) | 2022.02.18 |
Epoxy, Epoxy에 Databinding 사용 (RecyclerView 쉽게 사용) (0) | 2022.02.14 |
Android Room Database / 룸 데이터 베이스 (0) | 2022.01.14 |
Navigation (네비게이션) / Jetpack (0) | 2021.08.30 |