-
Hilt 공식문서 훑어보기2 Hilt ComponentsIT/android 2024. 12. 8. 21:58SMALL
컴포넌트 계층구조
전통적인 Dagger 와는 달리 Hilt 사용자는 Dagger 컴포넌트를 직접 정의하거나 인스턴스화 시키지 않습니다. 대신에 Hilt 는 미리 정의된 컴포넌트를 제공합니다. Hilt 는 안드로이드 애플리케이션의 다양한 수명주기에 자동으로 통합되는 미리 정의된 컴포넌트들의 셋과 함께 제공됩니다.
아래의 다이어그램은 표준 hilt 컴포넌트 계층을 보여줍니다.
Scope 어노테이션은 바인딩을 해당 컴포넌트의 수명주기에 바인딩 하기 위해서 사용되며, 화살표은 컴포넌트의 하위 컴포넌트를 가리킵니다. 일반적으로는 자식 컴포넌트의 바인딩은 부모 컴포넌트의 모든 바인딩에 종속성을 가질 수 있습니다.
인젝션을 위한 컴포넌트
@AndroidEntryPoint 같은 hilt API를 사용할때 표준 hilt 컴포넌트들은 주입기로서 사용이 됩니다. 주입기로서 사용되는 컴포넌트들은 어떠한 binding 이 보여질지를 결정하게 됩니다. 컴포넌트들의 사용처는 아래 테이블과 같습니다.
Component Injector for SingletonComponent Application ViewModelComponent ViewModel ActivityComponent Activity FragmentComponent Fragment ViewComponent View ViewWithFragmentComponent View with @WithFragmentBindings ServiceComponent Service 컴포넌트의 수명
컴포넌트의 수명은 매우 중요한데요 그 이유는 아래 두가지 중요한 방법으로 바인딩의 수명과 연관되어 있기 때문입니다.
1. 바인딩의 수명을 컴포넌트의 생성과 소멸 사이로 제한합니다.
2. 주입된 값들을 언제 사용할 수 있는지 나타냅니다. 예를들면 @Inject 필드가 언제 null이 아닌지.
컴포넌트의 생명주기는 일반적으로 해당 안드로이드 클래스 인스턴스의 생성과 소멸에 의해 결정됩니다.
다음은 클래스 인스턴스의 생성과 소멸에따른 바인딩의 수명 예제입니다.
// ApplicationComponent (SingletonComponent) @HiltAndroidApp class MyApplication : Application() { @Inject lateinit var someService: SomeService // Application 생성 시 주입되고, 앱이 종료될 때까지 유지 } // ActivityComponent @AndroidEntryPoint class MainActivity : AppCompatActivity() { @Inject lateinit var someHelper: SomeHelper // Activity onCreate에서 주입되고, onDestroy에서 해제 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate() // 이 시점 이후에 someHelper 사용 가능 } } // FragmentComponent @AndroidEntryPoint class MyFragment : Fragment() { @Inject lateinit var someUtil: SomeUtil // Fragment onAttach에서 주입되고, onDestroy에서 해제 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 이 시점에서 someUtil 안전하게 사용 가능 } }
스코프된 바인딩 vs un스코프된 바인딩
기본적으로 대거에서 모든 바인딩들은 unscoped 입니다. 이것의 뜻은 바인딩이 요청될때마다 새로운 인스턴스를 생성한다는 의미입니다.
그러나 Dagger 는 scoped 바인딩 도 허용을 하는데요, scoped 바인딩은 컴포넌트당 한번만 생성이 되게 됩니다.
// This binding is "unscoped". // Each request for this binding will get a new instance. class UnscopedBinding @Inject constructor() { } // This binding is "scoped". // Each request from the same component instance for this binding will // get the same instance. Since this is the fragment component, this means // each request from the same fragment. @FragmentScoped class ScopedBinding @Inject constructor() { }
주의사항!: 흔히 오해하는것은 모든 Fragment 인스턴스가 @FragmentScoped 로 범위가 지정된 바인딩의 동일한 인스턴스를 공유한다는 것입니다. 하지만 이것은 사실이 아닌데요, 각 Frgament 인스턴스들은 컴포넌트의 새 인스턴스를 가져오므로, scoped 바인딩의 새 인스턴스를 가져오게 됩니다. 즉 Fragment 인스턴스는 각각 자신만의 새로운 컴포넌트를 가지기때문에 다른 의존성들도 새롭게 가집니다. 예를들면 다음과 같습니다
@FragmentScoped class MyHelper @Inject constructor() { var count = 0 } // Fragment A (첫 번째 인스턴스) @AndroidEntryPoint class MyFragment : Fragment() { @Inject lateinit var helper: MyHelper // helper.count = 0 } // Fragment A (두 번째 인스턴스) @AndroidEntryPoint class MyFragment : Fragment() { @Inject lateinit var helper: MyHelper // 새로운 MyHelper 인스턴스, helper.count = 0 }
LIST'IT > android' 카테고리의 다른 글
Gradle 훑어보기1 - 기본사항 (1) 2024.12.14 DoveLetter interview question 훑어보기1 (1) 2024.12.09 Hilt 이해를 위한 Dagger 기반지식 쌓기1 (2) 2024.12.08 Hilt 공식문서 훑어보기1 (0) 2024.12.08 Composable의 수명주기 (0) 2024.11.21