Development record of developer who study hard everyday.

레이블이 안드로이드hilt인 게시물을 표시합니다. 모든 게시물 표시
레이블이 안드로이드hilt인 게시물을 표시합니다. 모든 게시물 표시
, , ,

Dagger2에서 Hilt로 이전하기(migration)

Dagger2에서 Hilt로 이전하기

안드로이드 블로그

회사프로젝트에 쓰이는 베이스소스가 있다.

지금 다니는 회사는 웹앱이 주 업무라서 베이스소스에 푸시알림이나 권한 등 기본적인 기능이 미리 세팅이 되어있다.

MVVM 패턴으로 구현이 되어있고 종속성주입에는 Dagger2 라이브러리가 사용된다.

하지만 이제 Dagger2는 지는 해이고 Hilt를 대부분 사용하기 때문에 이번기회에 migration하기로 했다.


1. Hilt 설정

buildscript {

ext.hilt_version = '2.44'

repositories {
...
}
dependencies {
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

👆 프로젝트수준 build.gradle에 위와 같이 hilt를 설정한다.



apply plugin: 'com.google.dagger.hilt.android'

android {

}

kapt {
correctErrorTypes = true
}

dependencies {

//hilt
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"


}

👆 앱수준 build.gradle에 위와 같이 hilt를 설정한다.


2. Application 수준에서 이전

가장 큰 Scope인 Application 수준에서의 이전을 시작한다.

먼저, Application 객체에 가서 Hilt를 사용한다고 알려주자

@Singleton
@HiltAndroidApp
class MainApplication @Inject constructor() : BaseApplication() {

// private lateinit var applicationInjector: AppComponent

override val configPath = "config.json"

override fun onCreate() {
// applicationInjector = DaggerAppComponent.factory().create(this)
super.onCreate()

}

// override fun applicationInjector(): AndroidInjector<out DaggerApplication> = applicationInjector

// fun appComponent() = applicationInjector
}

👆 Dagger와 관련된 코드는 다 없애준다.

솔직히, 나도 Dagger2를 사용해본적은 없기때문에 주석처리한 코드가 정확히 무슨 역할을 하는지는 잘 모른다.

하지만 종속성 주입을 위한 컴포넌트를 만드는 것이라는 것은 안다.

그리고 Hilt는 컴포넌트를 자동으로 생성해주기 때문에 저런 코드가 다 필요가 없다.

abstract class BaseApplication : /*Dagger*/Application() {
abstract val configPath: String

override fun onCreate() {
super.onCreate()
Config.init(this, configPath)
}
}

👆 BaseApplication 클래스에서도 원래 DaggerApplication()을 상속했지만 없애주고 Application()을 상속하도록한다.


이제 AppComponent에 설치되어있던 모듈을 SingletonComponent에 설치해주자.

SingletonComponent는 Hilt에서 Application Scope에서 종속성 주입을 하기위해 만드는 Container를 말한다.

@Singleton
@Component(modules = [
AndroidInjectionModule::class,
ActivityModule::class,
FragmentModule::class,
ServiceModule::class,
ViewModelModule::class,
AppModule::class,
AppBindModule::class,
NetworkModule::class
])
interface AppComponent : AndroidInjector<MainApplication> {
@Component.Factory
interface Factory {
fun create(@BindsInstance application: MainApplication): AppComponent
}

override fun inject(app: MainApplication)
}

👆 Dagger2에 사용되는 AppComponent의 코드이다.

AppComponent에 총 8개의 모듈이 설치가 되어있다.

이 모듈 클래스에 가서 @InstallIn(SingletonComponent::class) 어노테이션을 등록한다.

(AndroidInjectionModule은 Dagger2 기본 모듈이라 무시;;)


- ActivityModule

@InstallIn(SingletonComponent::class)
@Module
abstract class ActivityModule {

@ContributesAndroidInjector
abstract fun contributeMainActivity(): MainActivity
}

- FragmentModule

@InstallIn(SingletonComponent::class)
@Module
abstract class FragmentModule {

}

-ServiceModule

@InstallIn(SingletonComponent::class)
@Module
abstract class ServiceModule {

@ContributesAndroidInjector
abstract fun contributeFCMMessagingService(): FCMMessagingService
}

-ViewModelModule

@InstallIn(SingletonComponent::class)
@Module(includes = [
ViewModelFactoryModule::class
])
abstract class ViewModelModule {

@Binds
@IntoMap
@ViewModelKey(MainVM::class)
abstract fun bindMainVM(mainVM: MainVM): ViewModel
}

@MustBeDocumented
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)

-AppModule

@InstallIn(SingletonComponent::class)
@Module(includes = [
ApiServiceModule::class,
RepositoryModule::class
])
class AppModule {

}

-AppBindModule

@InstallIn(SingletonComponent::class)
@Module(includes = [
BaseBindModule::class
])
abstract class AppBindModule {

@Binds
abstract fun bindAppApplication(application: MainApplication): Application

@Binds
@IntoSet
abstract fun provideTokenInterceptor(tokenInterceptor: TokenInterceptor): Interceptor
}

-NetworkModule

@Module
@InstallIn(SingletonComponent::class)
class NetworkModule {

@Provides
fun provideClient(interceptors: Set<@JvmSuppressWildcards Interceptor>): OkHttpClient {

val client = OkHttpClient.Builder().apply {
connectTimeout(Config.INSTANCE.connectTimeOut, TimeUnit.MILLISECONDS)
readTimeout(Config.INSTANCE.readTimeOut, TimeUnit.MILLISECONDS)
writeTimeout(Config.INSTANCE.writeTimeOut, TimeUnit.MILLISECONDS)

if (interceptors.isNotEmpty()) {
interceptors.forEach {
addInterceptor(it)
}
}
}

val logging = HttpLoggingInterceptor().apply {
setLevel(HttpLoggingInterceptor.Level.BODY)
}
client.addNetworkInterceptor(logging)

return client.build()
}

@Provides
fun provideRetrofit(client: OkHttpClient): Retrofit {
return Retrofit.Builder().apply {
baseUrl(Config.INSTANCE.apiUrl)
client(client)
}.apply {
addCallAdapterFactory(RxJava2CallAdapterFactory.create())
addConverterFactory(GsonConverterFactory.create())
}.build()
}
}

👉 이렇게 AppComponent에 있는 모듈들을 전부 SingletonComponent에 설치해주었다.


그리고 Module에 설치된 Module이 또 있다;;

난 dagger2를 몰라서 왜 이렇게 만든지는 모르지만 아무튼 AppComponent와 관련된 Module은 싹다 SingletonComponent에 InstallIn해준다.


-ViewModelFactoryModule

@InstallIn(SingletonComponent::class)
@Module
abstract class ViewModelFactoryModule {

@Binds
abstract fun bindViewModelFactory(factory: DaggerViewModelFactory): ViewModelProvider.Factory

}

-ApiServiceModule

@InstallIn(SingletonComponent::class)
@Module
class ApiServiceModule {

@Singleton
@Provides
fun provideCommonService(retrofit: Retrofit): CommonService = retrofit.create(CommonService::class.java)
}

-RepositoryModule

@InstallIn(SingletonComponent::class)
@Module
class RepositoryModule {

@Singleton
@Provides
fun providePreferenceRepository(application: MainApplication, @ApplicationContext context: Context): PreferenceRepository =
PreferenceRepository(PreferenceManager(application, context))

@Singleton
@Provides
fun provideCommonRepository(service: CommonService): CommonRepository = CommonRepository(service)
}

-BaseBindModule

@Module
class BaseBindModule{}


AppComponent에 있는 Module을 전부 hilt가 자동으로 생성해주는 SingletonComponent에 설치했다.

따라서 AppComponent는 더이상 필요가 없다.

AppComponent 클래스를 지운다.


3. Hilt로 ViewModel 주입

힐트를 사용하면 ViewModel을 아주 쉽게 주입할 수 있다.

Hilt로 migration 중인 이 프로젝트에는 MainVM 하나 밖에 없다.

클래스명에 @HiltViewModel 만 적어주면 끝이다.

@HiltViewModel
class MainVM @Inject constructor(
application: MainApplication,
@ApplicationContext context: Context
) : BaseViewModel(application){
// val locationData = LocationLiveData(application)
val locationData = LocationLiveData(context)


}


이제, hilt로 ViewModel을 주입해주기때문에 Dagger2로 ViewModel을 주입하기위해 필요했던 잡다한 Module을 다 제거한다.

ViewModelFactoryModule, DaggerViewModelFactory, ViewModelFactoryModule 다 삭제한다.


이제 프로젝트를 빌드해보면 정상적으로 빌드가된다.


4. Dagger2의 SubComponent 제거

Dagger2에서는 SubComponent도 직접 만들어준다.

Hilt에서는 SubComponent를 자동으로 만들어주기 때문에 이 녀석들을 전부 제거하자.


-ActivityModule 삭제

@InstallIn(SingletonComponent::class)
@Module
abstract class ActivityModule {

@ContributesAndroidInjector
abstract fun contributeMainActivity(): MainActivity
}

-FragmentModule 삭제

@InstallIn(SingletonComponent::class)
@Module
abstract class FragmentModule {

}

-ServiceModule 삭제

@InstallIn(SingletonComponent::class)
@Module
abstract class ServiceModule {

@ContributesAndroidInjector
abstract fun contributeFCMMessagingService(): FCMMessagingService
}

-ViewModelModule 삭제

@InstallIn(SingletonComponent::class)
@Module(includes = [
ViewModelFactoryModule::class
])
abstract class ViewModelModule {

@Binds
@IntoMap
@ViewModelKey(MainVM::class)
abstract fun bindMainVM(mainVM: MainVM): ViewModel
}

@MustBeDocumented
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)


5. Dagger2 라이브러리 삭제

    // Dependency Inject - Dagger2
// def dagger_version = "2.45"
// implementation "com.google.dagger:dagger:$dagger_version"
// implementation "com.google.dagger:dagger-android:$dagger_version"
// implementation "com.google.dagger:dagger-android-support:$dagger_version"
// kapt "com.google.dagger:dagger-compiler:$dagger_version"
// kapt "com.google.dagger:dagger-android-processor:$dagger_version"


끝났다!!

Dagger2 -> Hilt 마이그레이션을 해보니 Hilt가 얼마나 좋은 라이브러리인지 깨닫게되었다.

Hilt 공부할 때는 최초설정하는 코드가 많고 어렵다고 느껴졌는데 Dagger2에 비하면 천국이였다.


Share:
Read More
, , , , ,

안드로이드 힐트(hilt) 사용하기

안드로이드 Hilt 사용하기

안드로이드 블로그

회사에서 첫 신규 프로젝트를 하면서 MVVM 구조를 적용하게 되었다.

이에 거의 필수적인 요소인 Hilt를 처음 프로젝트에 적용해보았다.

그 동안 코드랩을 보면서 연습만해봤지 프로젝트에 적용하는 것은 처음이랑 긴장도 되었지만 무사히 프로젝트를 마치게 되었다.

다시 한 번 복습도 할 겸 Android Hilt를 적용하는 과정을 기록해보려한다.


1. classpath 추가

buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.4"
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10'

classpath 'com.google.dagger:hilt-android-gradle-plugin:2.38.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

프로젝트 수준 gradle 파일에 classpath를 추가해준다.


2. dependency와 plugins id 추가

plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
} dependencies {     //hilt     implementation "com.google.dagger:hilt-android:2.38.1"     kapt "com.google.dagger:hilt-compiler:2.38.1" }

앱 수준의 gradle 파일에 위와같이 plugin과 dependency를 추가해준다.


3. application 선언

@HiltAndroidApp
class MainApplication: Application() {

}

Application() 클래스를 상속하는 클래스를 만들어주고 @HiltAndroidApp 어노테이션을 추가해준다.

여기서 주의할 점이 매니페스트 파일에서도 이 클래스를 선언해주어야한다.

아래 코드 참고

<application
android:name="com.appg.joowon.MainApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.JooWon">


</application>


이제 힐트를 사용할 준비가 끝났다.

힐트는 Jetpack Architecture에 최적화 되어있는 Dependency Injection이기 때문에 MVVM을 적용하기에 더 편하고 간결하다.

다음 글에서는 액티비티, 뷰모델 등등 다양한 컴포넌트들에서 hilt를 이용하여 객체를 주입하는 방법을 소개하도록 하겠다.


Share:
Read More