의존성 주입이란? (Dependency Injection)
- 생성자 또는 메서드 등을 통해 외부로부터 생성된 객체를 전달받는것
특징
(1) 클래스간 결합도를 느슨하게 함
(2) 인터페이스 기반으로 설계되며, 코드를 유연하게 한다.
(3) Stub 또는 Mock 객체를 사요하여 단위 테스트를 하기가 더욱 쉬워진다.
1. 의존성 주입이 없는 코드
class MemoRepository {
private val db = SQLiteDatabase()
fun load (id:String) {...}
}
fun main() {
val repository = MemoRepository()
repository.load("8092")
}
2. 의존성을 외부로부터 주입 받음
class MemoRepository (private val db:Database) {
fun load(id: String) {...}
}
fun main() {
val db = SQLiteDatabase()
val repository = MemoRepository(db)
repository.laod("8092")
}
안드로이드 에서 의존성 주입이 어려운 이유는?
- Android 클래스가 Framework에 의해 인스턴스 화 됨
- Factory를 API28로 부터 제공, 하지만 현실적 이지 않음
Dagger2
- 자바와 안드로이드를 위한 강력하고 빠른 의존성 주입 프레임워크
- 컴파일 타임에 그래프를 구성 (어노테이션)
- 생성된 코드는 명확하고 디버깅이 강함
- 리플렉션 X, 런타임 바이트 코드 생성X
- 생명주기와 객체간의 오브젝트 간에 공유할수 있게 제공함.
- 자원 공유의 단순화
- 작은 라이브러리 크기
의존성 주입 프레임워크의 궁극적인 목표
- 정확한 사용방법을 제공
- 쉬운 설정 방법
- 중요한 것들에 집중할 수 있도록 함
Hilt
- 애플리케이션에서 DI를 사용하는 표준적인 방법을 제공함.
- Dagger 사용의 단순화
- 표준화된 컴포넌트 세트와 스코프로 설정과 가독성/이해도 쉽게 만들기
- Dagger2 기반의 라이브러리 -> 보일러플레이트 코드 감소
- 프로젝트 설정 간소화
- 쉬운 모듈 탐색과 통합 환경 제공 -> 개선된 테스트 환경
- 안드로이드 스튜디오 4.2 부터는 그래프 상으로 제공 받음
Setting
1. 안드로이드 Gradle 모듈 build.gradle 파일 (Module:app)
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
apply plugin: 'com.android.application'
dependencies {
implementation "com.google.dagger:hilt-android:2.28-alpha"
kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
}
2. 프로젝트 최상위 Build.gradle 파일 (project:(프로젝트명))
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
}
}
Quick Setup
- @Inject : 의존성 주입을 하겠다는것을 의미
class MemoRepository @Inject constructor(
private val db:MemoDatabase
){
fun load(id:String) {
}
}
- @HiltAndroidApp : 모든 의존성 주입의 시작점 (있어야하는 부분)
@HiltAndroidApp
class MemoApp : Application()
- @Inject : 인젝트 어노테이션으로 되어있는것을 가져옴 (AndroidEntryPoint, Inject)
@AndroidEntryPoint
class MemoActivity : AppCompatActivity() {
@Inject lateinit var repository: MemoRepository
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_memo2)
repository.load("YHLQMDLG")
}
}
- @Provides : 메모 액티비티에서 메모 레파지토리를 주입 가능
@InstallIn(ApplicationComponent::class)
@Module
object DataModule {
@Provides
fun provideMemoDB(@ApplicationContext context: ApplicationContext) = Room.databaseBuilder(context, MemoDatabase::class.java, "Memo.db")
.build()
}
Object graph
- @HiltAndroidApp : ApplicationComponent 가 먼저 생성
- @InstallIn : 모듈 클래스에 추가한 해당 컴포넌트 모듈이 생성
- @AndroidEntryPoint : ApplicationComponent 하위 컴포넌트인 ActivityComponent 가 생성되고 MemoRepository 를 주입 받음
Hilt Annotation
- @HiltAndroidApp : Hilt 코드 생성을 시작, 반드시 Application 클래스에서 추가한다.
@HiltAndroidApp
class MemoApplication : Application() {
override fun onCreate()
super.onCreate() // 의존성 주입은 super.onCreate()에서 이루워짐 (bytecode 변환)
}
}
※ Application 타입을 상속을 받아서 진행 하고 싶다면? (BaseApplication)
@HiltAndroidApp(Application::class)
class MyApplication : Hilt_MyApplication()
- @AndroidEntryPoint : 어노테이션이 추가된 안드로이드 클래스에 DI 컨테이너를 추가함 (@HiltAndroidApp의 설정후 사용이 가능)
※ @HiltAndroidApp -> Component 생성
※ @AndroitEntryPoint -> Subcomponent 생성
※ @AndroidEntryPoint 를 지원하는 타입
(1) Activity
(2) Fragment
(3) View
(4) Service
(5) BroadcaseReceiver
Hilt Component Hierarchy
화살표 방향 기준으로 상위에서 하위로 가는 컴포넌트를 의미
하위 컴포넌트는 상위 컴포넌트가 가지고 있는 의존성에 접근이 가능함. (물론 직계 관계에서만 가능)
Hilt Scope
모듈 클래스에서 동일 인스턴스를 공유 할수가 있다.
Scoped Binding
- 인스턴스를 다른곳에서 동일하게 요청할경우에 두 액티비티가 서로 다른 인스턴스를 갖게됨
Singleton
- 단, 반드시 Hilt Scope 상에 명시되어 있는 스코프와 쌍을 이루어 야함. (ex : 액티비티는 액티비티끼리)
@Singleton
class MemoRepository @Inject constructor (
private val db: MemoDatabase
) {
fun load(id:String) {...}
}
- @InstallIn : Hilt가 생성하는 DI 컨테이너에 어떤 모듈을 사용할 지 가르킨다. (매우중요)
@Module 클레스에 @ InstallIn이 없을경우 컴파일 에러 발생
- 만약 엑티비티 컴포넌트, 프레그먼트가 둘다 필요할경우 상위 컴포넌트에 모듈을 설치 하는것. (ApplicationComponent)
- @EntryPoint : Hilt 가 지원하지 않는 클래스에서 의존성이 필요한 경우에 사용
(ex : ContentProvider, DFM, Dagger를 사용하지 않은 3rd-party 라이브러리 등)
(1) @EntryPoint는 인터페이스만 사용가능
(2) @InstallIn이 있어야함
(3) EntryPonts 클래스의 정적 메서드를 통해 그래프에 접근 한다.
//EntryPoint 생성하기
@EntryPoint
@InstallIn(ApplicationComponent::class)
interface FooBarInterface {
fun getBar(): Bar
}
//EntryPoint로 접근하기
val bar = EntryPoints.get(
applicationContext, FooBarInterface::class.java
).getBar()
그 외 Hilt에 대한 내용들
- AndroidX Extensions (ViewModel)
1. ViewModel
class MemoViewModel @ViewModelInject constructor (
private val repository: MemoRepository,
@Assisted private val savedStateHandle : SavedStateHandle
) : ViewModel() {...}
@AndroidEntryPoint
class MemoActivity : AppCompatActivity() {
private val viewModel : MemoViewModel by viewModels()
}
출처 : https://www.youtube.com/watch?v=gkUCs6YWzEY
'ANDROID' 카테고리의 다른 글
Android UI - Animation (Transition) (0) | 2020.09.27 |
---|---|
Android UI - Animation (Button) (0) | 2020.09.27 |
Android UI - Animation (Loading) (0) | 2020.09.19 |
ViewModel + LiveData + DataBinding 을 적용 해보자 (0) | 2020.08.17 |
간단하게 RecyclerView 를 달아보자 (0) | 2020.07.31 |