- 의존성 주입이라는 개념이 있다. 객체가 필요한 의존 객체를 외부에서 주입받아 사용하는 설계를 말한다.
- 보통 일반적으로 수동적 의존성 주입 처리가 진행된다. 하지만 지연 초기화, nullable 처리, 코드 중복량 증가에 대한 문제점들이 시간이 지날수록 드러난다.
- 이런 문제점들을 줄이기 위해 DI를 컴파일 시점에서 처리해 런타임 성능을 최적화하고, 어노테이션으로 객체 간 의존 관계를 관리할 수 있는 라이브러리 Hilt가 있다.
https://developer.android.com/training/dependency-injection/hilt-android?hl=ko
Hilt를 사용한 종속 항목 삽입 | App architecture | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. Hilt를 사용한 종속 항목 삽입 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Hilt는 프로젝트에서 종속
developer.android.com
- 서비스 프로젝트 내에 이런 의존성과 관련된 코드들이 곳곳에 존재했다.
companion object {
private var INSTANCE: Database? = null
fun getInstance(context: Context): Database? {
synchronized(Database::class) {
INSTANCE = Room.databaseBuilder(
context.applicationContext,
Database::class.java,
ROOM_FILE_NAME
).build()
}
return INSTANCE
}
}
- Room DB를 사용 중인데, 그 인스턴스를 초기화하기 위한 코드이다. 빌더 코드뿐만 아니라 늦은 초기화와 null 체크에 대해 신경 써주어야 한다.
private val searchDatabase = Database.getInstance(context)
private val searchDao = searchDatabase?.recentItemSearchDao()
- Repository에서 DAO에 접근하는 코드 또한 매번 구성해주어야 한다.
private val searchRepo = SearchRepository(AppCore.context)
- UI에서 접근할 때도 필요한 파라미터를 넣어서 매번 초기화해 줬다.
- 또한 기존 프로젝트에서 MVVM 구성을 제대로 지키지 않고 있었다... (Model + View + [ViewModel] -> 이것이 없다.)
- 먼저 Hilt 사용을 위해 gradle 등록을 진행한다. 프로젝트 수준에는 아래 내용을 추가했다.
plugins {
id("com.google.dagger.hilt.android") version "2.46" apply false
}
- 앱 수준에는 아래 내용들을 추가했다.
apply plugin: 'com.google.dagger.hilt.android'
.
.
.
implementation("com.google.dagger:hilt-android:2.46")
kapt("com.google.dagger:hilt-android-compiler:2.46")
- 다음으로 Application 클래스에 Hilt를 사용하기 위한 어노테이션을 설정한다.
@HiltAndroidApp
public class AppCore extends MultiDexApplication {
- 존재하지 않는다면 반드시 해당 클래스를 생성해줘야 한다. 이를 통해 앱 전체에서 사용할 싱글톤 DI 컨테이너를 생성하며, 앱 생명주기 동안 유지된다.
- 다음으로 AppModule Singleton을 구성해 준다.
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
@Provides
@Singleton
fun providesDatabase(
@ApplicationContext context: Context
): Database = Room.databaseBuilder(
context,
Database::class.java,
ROOM_FILE_NAME
).build()
@Provides
@Singleton
fun providesDatabaseDao(db: Database): Dao = db.searchDao()
}
- @Module: 이 클래스가 Hilt의 의존성 제공자임을 나타낸다. 이 클래스 내부에서 Retrofit, RoomDB에 대한 초기 구성이 진행된다.
- @InstallIn(SingletonComponent::class): 이 모듈에서 제공하는 의존성 객체들의 생명 주기(scope)를 앱 전체(Singleton)로 지정한다.
- @Provides: 이 함수가 객체를 생성해서 의존성으로 제공한다는 것을 나타낸다.
- @Singleton: 앱 전체에서 하나의 인스턴스만 공유하도록 지정한다. 기존에도 companion object로 선언되었던 부분이다.
- @ApplicationContext: Hilt가 Context를 주입할 때, Application Context를 지정한다.
- 이를 통해 Hilt가 DB 인스턴스를 생성하고 이후에 DAO까지 주입할 수 있도록 처리된다.
- 기존의 Database class에서는 abstract func 구조만 남겨지게 된다.
@Database(entities = [Search::class], version = 1)
abstract class Database: RoomDatabase() {
abstract fun searchDao(): SearchDao
}
- 기존의 Repository는 DAO 접근 코드가 사라지고 @Inject를 통해 파라미터 들어온 DAO를 의존성 주입받아 바로 접근할 수 있다.
class SearchRepository @Inject constructor(
private val searchDao: SearchDao
) {
fun fetchAllRecentSearch(): List<String> = searchDao.getAllSearches().map { it.title }
suspend fun delete(title: String) = searchDao.delete(title)
suspend fun upsert(title: String) = searchDao.updateOrInsert(RecentItemSearch(title, System.currentTimeMillis()))
}
- 또한 Repository에서 UI 활용 가능한 형태로 데이터를 Fetch 해줄 ViewModel을 구성한다.
@HiltViewModel
class SearchViewModel @Inject constructor(
private val repository: SearchRepository
): ViewModel() {
fun getAllRecentSearch() = repository.fetchAllRecentSearch()
fun upsert(title: String) {
viewModelScope.launch(Dispatchers.IO) {
repository.upsert(title)
}
}
fun delete(title: String) {
viewModelScope.launch(Dispatchers.IO) {
repository.delete(title)
}
}
}
- @HiltViewModel은 해당 ViewModel 클래스가 Hilt에 의해 관리되어야 함을 나타내며 ViewModel 객체를 생성할 때, 필요한 의존성들을 자동으로 주입해 준다.
@AndroidEntryPoint
class StoreItemSearchActivity : ComponentActivity() {
- 해당 ViewModel을 사용하려는 Activity에서는 위와 같은 어노테이션을 선언해 준다.
- @AndroidEntryPoint는 Hilt를 사용하는 Android 컴포넌트(Activity, Fragment 등)에 붙이는 어노테이션이다.
즉, Hilt가 의존성을 주입할 수 있도록 만드는 진입점(Entry Point)이라는 뜻.
private val viewModel: RecentItemSearchViewModel by viewModels()
- 그 이후에 Hilt로 의존성 주입이 적용된 ViewModel을 Activity에서 위와 같이 접근이 가능하다.