Advertisement

Android DI With Hilt

Feb. 16, 2023
Advertisement

More Related Content

Recently uploaded(20)

Advertisement

Android DI With Hilt

  1. 발표자 : 이상수(vviprogrammer@gmail.com) (2022-06) DI(Dependency Injection) With Dagger-Hilt
  2. DI 란? DI = Dependency Injection = 의존성(종속성) 주입 • 한 클래스가 다른 클래스에 대한 참조가 필요할 때 • Ex) Car 클래스에서 Engine 클래스에 대한 참조가 필수 일때, 의존성을 가지게 된다 class Engine() {} class Car { private val engine = Engine() // 의존성 }
  3. DI 란? 클래스가 필요한 의존성(인스턴스)을 얻는 세 가지 방법 1. 필요한 클래스 인스턴스 직접 생성 및 초기화 2. 다른 곳으로 부터 끌어오기 (Ex. Context 게터, getSystemService() 등) 3. 파라미터로 제공 받기 (생성자나 함수 등 에서 필요한 의존성을 전달 받기)
  4. DI 란? DI(의존성 주입) 을 하지 않는 코드 class Car { private val engine = Engine() fun start() { engine.start() } } fun main(args: Array) { val car = Car() car.start() } • Car와 Engine은 강하게 커플링 됨 • 테스트가 어려움 (*Test Double 등) * Test Double : 테스트를 진행하기 어려운 경우 이를 대신해 테스트를 진행할 수 있도록 만들어주는 객체를 말한다.
  5. DI 란? DI(의존성 주입) 을 사용한 코드 class Car(private val engine: Engine) { fun start() { engine.start() } } fun main(args: Array) { val engine = Engine() val car = Car(engine) car.start() } • 재사용성 ⬆ (Car 클래스) • Ex) ElectricEngine • 테스트 쉬움 • Ex) FakeEngine
  6. DI 란? Android 에서 주로 사용하는 2가지 DI 방법 1. 생성자 주입(Constructor Injection) 2. 필드 주입(Field Injection)(or Setter Injection) class Car { lateinit var engine: Engine fun start() { engine.start() } } fun main(args: Array) { val car = Car() car.engine = Engine() // 필드 주입 car.start() }
  7. Automated dependency injection 수동 의존성 주입의 문제점 • 큰 프로젝트에서 올바른 DI 를 해주기 위해서는 많은 양의 boilerplate code 필요 • 앱의 수명 주기를 관리하는 지정 컨테이너를 직접 작성해야하고 유지해야함
  8. Automated dependency injection 의존성 주입 라이브러리 • 의존성 생성과 제공 프로세스를 자동화 하는 라이브러리 • 런타임에 의존성을 주입하는 리플렉션 기반 솔루션 • 컴파일 타임에 의존성을 연결하는 코드를 생성하는 정적 솔루션 • Google 의 Dagger • Java, Kotlin 및 Android 용 DI로 널리 사용 • 완전 정적 컴파일 타임 DI로 리플렉션 기반 솔루션(Guice 등)의 성능 문제를 해결
  9. Use Hilt in your Android app Android 의존성 주입을 위한 Jetpack의 권장 라이브러리 • Android 클래스 (Activity, Fragment 등) 에 표준 컨테이너를 제공 • 자동으로 수명주기를 관리하여 DI 를 적용 • 2021년 4월 부터 stable 버전 릴리즈 (현재 2.38.1ver)
  10. Use Hilt in your Android app Dagger의 bene fi t을 그대로 가지면서 + 𝜶 • compile-time correctness • runtime performance • scalability • Android Studio support that Dagger provides • + Boilerplate Code 감소 (개발자가 작성해야 하는 코드 감소) • + Jetpack Library 지원 (별도 컨테이너 구현 필요 X) • + Dagger2 의 러닝커브에 비해 쉬운 사용성
  11. Dagger-Hilt 지원하는 Android Class + Jetpack Libraries • Application (by using @HiltAndroidApp) • Activity • Fragment • View • Service • BroadcastReceiver ViewModel (by using @HiltViewModel) Navigation Compose WorkManager
  12. Dagger-Hilt 적용 해보기 Application class MyApplication : Application(), HasAndroidInjector { @Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> { return androidInjector } override fun onCreate() { super.onCreate() DaggerAppComponent.factory() .create(this) .inject(this) } } Dagger @HiltAndroidApp class MyApplication : Application() { /* … */ } Hilt
  13. Dagger-Hilt 적용 해보기 Activity, Fragment (Module 사용) class MyActivity : AppCompatActivity(), HasAndroidInjector { @field:Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> protected set override fun androidInjector(): AndroidInjector<Any> { return androidInjector } Dagger @Singleton @Component( modules = [ AndroidSupportInjectionModule::class, AppModule::class, ActivityModule::class ] ) abstract class AppComponent : AndroidInjector<MyApplication> { @Component.Factory abstract class Factory : AndroidInjector.Factory<MyApplication> } @Module interface ActivityModule { @ActivityScope @ContributesAndroidInjector(modules = [MyModule::class]) fun myActivity(): MyActivity @Module abstract class MyModule { @Binds abstract fun bindFragmentActivity(activity: MyActivity): FragmentActivity 컴포넌트 정의 (안드로이드 지원 모듈, Activity, App 모듈) Activity Scope 를 가지는 모듈 해당 Activity 모듈에 bind 및 provide 할 종속성 정의 HasAndroidInjector 를 반드시 구현, 및 @Inject 로 주입 받기
  14. Dagger-Hilt 적용 해보기 Component scopes
  15. Dagger-Hilt 적용 해보기 Activity, Fragment (Module 사용) @AndroidEntryPoint class ExampleActivity : AppCompatActivity() { @Inject lateinit var analytics: AnalyticsService ... } // If AnalyticsService is an interface. @Module @InstallIn(SingletonComponent::class) abstract class AnalyticsModule { @Singleton @Binds abstract fun bindAnalyticsService( analyticsServiceImpl: AnalyticsServiceImpl ): AnalyticsService } @Inject 로 주입 받기 Hilt Module 이 Bind 할 Hilt 컴포넌트 지정 @Bind 로 주입 // If you don't own AnalyticsService. @Module @InstallIn(SingletonComponent::class) object AnalyticsModule { @Singleton @Provides fun provideAnalyticsService(): AnalyticsService { return Retrofit.Builder() .baseUrl("https://example.com") .build() .create(AnalyticsService::class.java) } } @Provides 로 의존성 제공 object Fragment 도 동일
  16. Dagger-Hilt 적용 해보기 Jetpack ViewModel public class DaggerAwareViewModelFactory @Inject public constructor( private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>> ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel?> create(modelClass: Class<T>): T { var creator = creators[modelClass] if (creator == null) { for ((key, value) in creators) { if (modelClass.isAssignableFrom(key)) { creator = value break } } } if (creator == null) { error("unknown model class $modelClass") } try { return creator.get() as T } catch (e: Exception) { throw e } } } @MapKey public annotation class ViewModelKey(val value: KClass<out ViewModel>) Dagger abstract class MyModule { @Binds fun provideViewModelFactory(factory: DaggerAwareViewModelFactory): ViewModelProvider.Factory @Binds @IntoMap @ViewModelKey(MyViewModel::class) abstract fun bindMyViewModel(viewModel: MyViewModel): ViewModel @Binds abstract fun bindFragmentActivity(activity: MyActivity): FragmentActivity … @Module companion object { @JvmStatic @ViewModelInject @Provides fun provideMyViewModel( activity: MyActivity, viewModelFactory: ViewModelProvider.Factory ): MyViewModel = ViewModelProvider(activity, viewModelFactory).get() Map Key 를 사용하는 ViewModelFactory 정의 ViewModelFactory 를 통해 ViewModel 주입 @field:ViewModelInject @field:Inject public lateinit var viewModel: VM protected set Map 을 사용한 멀티 바인딩 Activity or Fragment 에서 Inject 하여 사용 여전히 매번 모듈 작성에 대한 Boilerplate Code 발생
  17. Dagger-Hilt 적용 해보기 Jetpack ViewModel @AndroidEntryPoint class MyActivity : AppCompatActivity() { val viewModel: MyViewModel by viewModels() @HiltViewModel class MyViewModel @Inject constructor( MyRepository: MyRepository ) : ViewModel() Hilt by using @HiltViewModel by viewModels() KTX 확장을 사용하여 주입 가능 모듈 작성 시 발생하는 Boilerplate Code 없음 Jetpack 라이브러리를 지원, 직관적인 사용 가능
  18. Migrating to Hilt 스마일페이 -> G마켓, 옥션 ? • Dagger and Hilt code can coexist in the same codebase. • However, in most cases it is best to use Hilt to manage all of your usage of Dagger on Android.
  19. Reference • https://developer.android.com/training/dependency-injection • https://developer.android.com/training/dependency-injection/hilt-android • https://developer.android.com/training/dependency-injection/hilt-jetpack • https://velog.io/@wlsdud2194/what-is-di • https://tecoble.techcourse.co.kr/post/2020-09-19-what-is-test-double/ • https://velog.io/@ptm0304/ViewModel%EC%97%90- %EC%9D%98%EC%A1%B4%EC%84%B1- %EC%A3%BC%EC%9E%85%ED%95%98%EA%B8%B0-Dagger-2-Java • http://labs.brandi.co.kr/2021/04/27/kimdy3.html
  20. 감사합니다
Advertisement