일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Python
- 코루틴
- 쿠링
- ProGuard
- activity
- relay
- 백준
- Rxjava
- pandas
- 암호학
- architecture
- textfield
- Codeforces
- Coroutines
- TEST
- android
- Kotlin
- livedata
- boj
- MiTweet
- Hilt
- MyVoca
- 코드포스
- 프로그래머스
- GitHub
- androidStudio
- Compose
- Coroutine
- AWS
- Gradle
- Today
- Total
이동식 저장소
[Hilt] Android Entry Points 본문
주의: 이 블로그에서 말하는 바인딩은 ``@Binds``와 ``@Provides`` 모두를 의미합니다.
Entry Point란 변수를 주입받을 수 있는 클래스를 말한다. Hilt의 모태인 dagger에도 존재하는 유서깊은 개념이다.
``Application``에서 의존성 주입 기능을 활성화했다면 다른 안드로이드 클래스에서 변수를 주입받을 수 있다. 그런데 모든 클래스에서 주입받을 수 있는 건 아니고, 다음 클래스에서만 멤버를 주입받을 수 있다.
- Activity (``ComponentActivity``)
- Fragment (androidx ``Fragment``)
- View
- Service
- BroadcastReceiver
- ViewModel
사실 ``ViewModel``은 다른 5개의 클래스와는 약간 다른 방식이 사용된다. 다른 글에서 자세히 얘기하겠다. ContentProvider도 직접 지원하지는 않지만, dagger의 entry point에 직접 접근하면 멤버를 주입받을 수 있다. 여기서는 생략.
Android Entry Point와 dagger의 Entry Point는 다르다. 정확히는 dagger의 Entry Point가 상위의 개념이다.
다음은 ``Activity``에서 Hilt로 변수를 주입받는 방법이다. 다른 클래스에서도 똑같이 하면 된다.
@AndroidEntryPoint
class HomeActivity: ComponentActivity() {
// 바인딩이 SingletonComponent 또는 ActivityComponent에 설치되어야 한다.
@Inject lateinit var foo: Foo
override fun onCreate(savedInstance: Bundle?) {
// 여기서 주입된다.
super.onCreate()
// onCreate() 이후 사용 가능
foo.doSomething()
}
}
클래스 최상단에 ``@AndroidEntryPoint`` 어노테이션을 붙이고, 주입받을 변수에 ``@Inject``를 붙이면 된다.
단, 해당 객체를 제공하는 모듈이 적절한 component에 존재해야 한다. 위의 코드에서 ``Foo`` 타입을 제공하는 바인딩은 ``SingletonComponent`` 또는 ``ActivityComponent``에 설치되어야 한다. 하위 component에 설치하면 Hilt가 바인딩을 못 찾는다.
참고로 component마다 객체가 주입되는 위치가 다르다.
Retained Fragments
Fragment의 ``onCreate`` 함수 안에서 ``setRetainInstance(true)``를 호출하면 해당 fragment가 configuration change에서 살아남는다. 진짜? 처음 알았다.
그런데 Hilt를 사용하는 fragment는 절대로 retain되면 안 된다. 왜냐하면 Hilt를 사용하는 fragment는 fragment 안에서 사용된 component를 참조하는데, 이 component가 사실 ActivityComponent일 수도 있기 때문이다. 자식이 부모를 참조하는 이상한 일이 벌어지는 것이다. 또, scope된 바인딩과 provider 중 fragment에 주입된 것들이 메모리 누수를 일으킬 수 있다.
추측하기로는 Hilt에서 fragment가 retain되는지 아닌지 판단할 방법이 없는 듯하다. 구조적으로 불가능하다던가? 어쨌든 이런 문제에 대응하기 위해 Hilt fragment가 retain되면 exception이 발생한다.
물론 Hilt를 사용하지 않는 fragment는 얼마든지 retain될 수 있지만, 그마저도 자식 fragment에서 Hilt를 사용한다면 exception이 발생할 것이다.
# (클릭) Fragment가 retain되는 매커니즘
Configuration change가 발생하면 화면의 모든 activity와 fragment가 새로 만들어진다. 그런데 retain을 활성화하면 해당 fragment만 살아남아 새로운 activity에 부착된다. 위에서 말한 모든 문제는 fragment가 새로운 activity에 부착될 때 일어난다.
그래서 같은 activity 객체에 다시 attach되는 건 상관없다. 하지만 그냥 새 fragment를 만드는 게 낫다. 헷갈리지도 않고.
View에서 Fragment 바인딩을 사용하는 방법
View에서는 기본적으로 ``SingletonComponent``와 ``ActivityComponent``의 바인딩만을 사용할 수 있다. ``FragmentComponent``의 바인딩을 사용하고 싶다면 ``@WithFragmentBindings`` 어노테이션을 붙여야 한다.
@AndroidEntryPoint
@WithFragmentBindings
class MyView : MyBaseView {
// SingletonComponent, ActivityComponent,
// FragmentComponent, ViewComponent의 바인딩 사용 가능
@Inject lateinit var bar: Bar
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
init {
// bar 사용 가능
bar.doSomething()
}
override fun onFinishInflate() {
super.onFinishInflate();
// 다른 뷰 작업
}
}
'Primary > Android' 카테고리의 다른 글
[Hilt] Modules (0) | 2022.08.29 |
---|---|
[Hilt] ViewModels (0) | 2022.08.29 |
[Hilt] Application (0) | 2022.08.11 |
[Android] 계정명이 한글일 때 test error 해결법 (0) | 2022.07.26 |
[Android] Manifest 파일이란? (0) | 2022.07.25 |