SlideShare a Scribd company logo
발표자 : 이상수(vviprogrammer@gmail.com) (2022-06)
DI(Dependency Injection)
With Dagger-Hilt
DI 란?
DI = Dependency Injection = 의존성(종속성) 주입
• 한 클래스가 다른 클래스에 대한 참조가 필요할 때
• Ex) Car 클래스에서 Engine 클래스에 대한 참조가 필수 일때, 의존성을 가지게 된다
class Engine() {}
class Car {
private val engine = Engine() // 의존성
}
DI 란?
클래스가 필요한 의존성(인스턴스)을 얻는 세 가지 방법
1. 필요한 클래스 인스턴스 직접 생성 및 초기화
2. 다른 곳으로 부터 끌어오기 (Ex. Context 게터, getSystemService() 등)
3. 파라미터로 제공 받기 (생성자나 함수 등 에서 필요한 의존성을 전달 받기)
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 : 테스트를 진행하기 어려운 경우 이를 대신해 테스트를 진행할 수 있도록 만들어주는 객체를 말한다.
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
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()
}
Automated dependency injection
수동 의존성 주입의 문제점
• 큰 프로젝트에서 올바른 DI 를 해주기 위해서는 많은 양의 boilerplate code 필요
• 앱의 수명 주기를 관리하는 지정 컨테이너를 직접 작성해야하고 유지해야함
Automated dependency injection
의존성 주입 라이브러리
• 의존성 생성과 제공 프로세스를 자동화 하는 라이브러리
• 런타임에 의존성을 주입하는 리플렉션 기반 솔루션
• 컴파일 타임에 의존성을 연결하는 코드를 생성하는 정적 솔루션
• Google 의 Dagger
• Java, Kotlin 및 Android 용 DI로 널리 사용
• 완전 정적 컴파일 타임 DI로 리플렉션 기반 솔루션(Guice 등)의 성능 문제를 해결
Use Hilt in your Android app
Android 의존성 주입을 위한 Jetpack의 권장 라이브러리
• Android 클래스 (Activity, Fragment 등) 에 표준 컨테이너를 제공
• 자동으로 수명주기를 관리하여 DI 를 적용
• 2021년 4월 부터 stable 버전 릴리즈 (현재 2.38.1ver)
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 의 러닝커브에 비해 쉬운 사용성
Dagger-Hilt
지원하는 Android Class + Jetpack Libraries
• Application (by using @HiltAndroidApp)
• Activity
• Fragment
• View
• Service
• BroadcastReceiver
ViewModel (by using @HiltViewModel)
Navigation
Compose
WorkManager
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
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 로 주입 받기
Dagger-Hilt 적용 해보기
Component scopes
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 도 동일
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 발생
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 라이브러리를 지원, 직관적인 사용 가능
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.
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
감사합니다

More Related Content

PDF
2024 Trend Updates: What Really Works In SEO & Content Marketing
PPTX
Why use Dagger in Android
PPTX
Dagger 2.0 을 활용한 의존성 주입
PPTX
Dependency Injection 소개
PDF
Dagger with multi modules
PPTX
DI - Dependency Injection
PPTX
Spring boot DI
PDF
Side Effect in Compose (Android Jetpack)
2024 Trend Updates: What Really Works In SEO & Content Marketing
Why use Dagger in Android
Dagger 2.0 을 활용한 의존성 주입
Dependency Injection 소개
Dagger with multi modules
DI - Dependency Injection
Spring boot DI
Side Effect in Compose (Android Jetpack)
Ad

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 로 주입 받기
  • 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