2022년 10월 10일 월요일

Android Hilt 사용하기

Android Hilt 사용하기

Hilt 사용하기

Spring 에서 서비스단의 클래스를 그냥 어노테이션으로 선언만하고 막쓰던것 처럼 DI해주는 Dagger 라는 라이브러리를 기반으로 안드로이드에서 HILT로 새로 내놓았다고함. Dagger 는 Android용이 아니었기 때문에 안드로이드에 맞는 Hilt라는 것을 내놓은거다.

Dagger + Android LifeCycle = HILT 라고 생각해도됨.

구글이 Hilt currently supports the following Android classes:

  • Application (by using @HiltAndroidApp)
  • ViewModel (by using @HiltViewModel)
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

만 지원한다네… 뭐 다되는거 아님?

@Inject 어노테이션으로 주입과 사용 양쪽에 사용한다. 사용자가 만든 클래스의 경우에는 Hilt가 삽입하고자 하는 곳에 초기화해서 사용하기 위해서 constructor 에 선언해주면 된다. 다만 안드로이드의 경우 프레임워크이고 라이프사이클이 있어서 Module 과 IntallIn 이라는 어노테이션으로 프레임워크의 어떤 부분에 해당 모듈을 사용할건지를 선언해줘야 한다.

@Module이 클래스에 지정하는 어노테이션이라고 하면, 개개별 메소드에는 @Provide 또는 @Bind를 통해 다른곳에서 Injection하여 끌어다가 사용할수 있도록 한다.

@Provide : 실제 코드를 구현하여 메소드에서 뭔가 작업할때 Object계열읠 클래스일때는 이걸쓴다.
@Bind : Abstract 클래스에서는 메소드도 추상화 시키기 때문에, 실제 구현 코드는

  1. 종속성추가

https://developer.android.com/training/dependency-injection/hilt-android?hl=ko

  1. Application 클래스를 생성하고 HiltAndroidApp이라고 사용함을 지정, MainActivity에 EntryPoint지정

https://github.com/sugoigroup/android_hilt_example/commit/66e062ae622674d70ed511ab526b95637399aa1b

  1. 간단하게 사용해보자

https://github.com/sugoigroup/android_hilt_example/commit/84c20d9e6c87108ae378852eb900ca63785278bd

-------------------------------
//사용하고자 하는 클래스 변수에 Inject 를 지정
@Inject
lateinit var calcme: CalcMe
...

// Inject 지정에 의해 calcme변수에 CalceMe객체가 할동되어있다.
Log.e("Test", calcme.sumMe(1, 2).toString())

-------------------------------
//사용하고자 하는 클래스 변수에 Inject 를 지정

class CalcMe @Inject constructor() {
    fun sumMe(num1:Int, num2:Int) : Int {
        return num1 + num2
    }
}
  1. Module 간단한 사용.

https://github.com/sugoigroup/android_hilt_example/commit/112a15def5b62d2203bcfab40462ad3c82559185

--------------------------------
MyHiltModule.kt

@Module // Module은 클래스에 정의
@InstallIn(ApplicationComponent::class) //Application 의 라이프사이클에 맞추어 동작한다는 의미

class MyHiltModule {

    @Singleton
    @Provides
    // 리턴 타입이 MainApplication 이라면 이게 실행됨.
    fun provideMeApplication(@ApplicationContext app: Context): MainApplication  {
        // send data to server then receive results
        return app as MainApplication
    }

    @Singleton
    @Provides
    // 리턴 타입이 Int형이라면 이게 실행
    fun sendAndBringApiService(): Int {
        // send data to server then receive results
        return 200
    }

    @Singleton
    @Provides
    // 리턴 타입이 String 이라면 이게 실행
    fun saveToRepository(): String = "Saved"
}

--------------------------------
MainActivity.kt

    //변수에 Inject
    @Inject
    lateinit var myapp: MainApplication // MainApplication 을 리턴타입으로 하는 프로바이더 자동 삽입

    var resultCode: Int = 0
        @Inject set // Int 을 리턴타입으로 하는 프로바이더 자동 삽입

    @Inject
    lateinit var saveRepo: String // Int 을 리턴타입으로 하는 프로바이더 자동 삽입

    //출력
        Log.v("Test", "my app: ${myapp}")
        Log.v("Test", "result Code: ${resultCode}")
        Log.v("Test", "saveRepo result: ${saveRepo}")
  1. 같은 리턴타입의 프로바이더를 Qualifier 로 구분지어 호출하자.

https://github.com/sugoigroup/android_hilt_example/commit/f0df622bdddda5f8bb31db15509569c58774f7f8

--------------------------------
MyHiltModule.kt

@Module // Module은 클래스에 정의
@InstallIn(ApplicationComponent::class) //Application 의 라이프사이클에 맞추어 동작한다는 의미

class MyHiltModule {

    @Singleton
    @Provides
    // 리턴 타입이 MainApplication 이라면 이게 실행됨.
    fun provideMeApplication(@ApplicationContext app: Context): MainApplication  {
        // send data to server then receive results
        return app as MainApplication
    }

    @Singleton
    @Provides
    // 리턴 타입이 Int형이라면 이게 실행
    fun sendAndBringApiService(): Int {
        // send data to server then receive results
        return 200
    }

    @Singleton
    @Provides
    @SaveRepoAction
    // 리턴 타입이 String 이고 @SaveRepoAction 이라면 이게 실행
    fun saveToRepository(): String = "delete"

    @Singleton
    @Provides
    @DeleteRepoAction
    // 리턴 타입이 String 이고  @DeleteARepoction 이라면 이게 실행
    fun deleteToRepository(): String = "Saved"
}

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
internal annotation class SaveRepoAction

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
internal annotation class DeleteRepoAction
}

--------------------------------
MainActivity.kt

    //변수에 Inject
    @Inject @SaveRepoAction
    lateinit var saveRepo: String // Int 을 리턴타입으로 하는 프로바이더 자동 삽입

    @Inject @DeleteRepoAction
    lateinit var deleteRepo: String // Int 을 리턴타입으로 하는 프로바이더 자동 삽입

    //출력

        Log.v("Test", "deleteRepo result: ${deleteRepo}")
        Log.v("Test", "saveRepo result: ${saveRepo}")
  1. @bind로 사용자가 정의한 constructor를 써보자.

https://github.com/sugoigroup/android_hilt_example/commit/dfcd4f7e3b96efb2fa3274b98611665d2dd6e9c5

--------------------------------
MyHitAbs.kt

@Module
@InstallIn(ApplicationComponent::class)
abstract class MyHiltAbs{ //추상클래스를 모듈로 선언
    @Singleton
    @Binds  //PowerInterfaceImpl 을 powerImpl에 Inject하고, PowerInterface형식을 선언하는 곳에 구현제를 바인드 해준다.
    abstract fun bindMyComputer(powerImpl: PowerInterfaceImpl): PowerInterface
}

interface PowerInterface {
    fun getPower(): String
}

class PowerInterfaceImpl @Inject constructor(): PowerInterface {
    override fun getPower(): String {
        return "Low Power(<220)"
    }
}

// Binds에 의해 자동으로 powerImpl 변수는  PowerInterfaceImpl 구현체에 바인드 되어 진다.
class MyComputer @Inject constructor(private val powerImpl: PowerInterface) {
    fun powerStart(): String {
        return "${powerImpl.getPower()}" //구현체의 getPower가 실행된다.
    }
}


--------------------------------
MainActivity.kt

    @Inject
    lateinit var myComputerBinded: MyComputer; //Inject와 동시에 Bind에 지정된 구현체가 연결된다.

        Log.v("Test", "bind example: ${myComputerBinded.powerStart()}")

이밖에 ViewModel과의 연동도 가능한다.

저렇게 써놓으면 Hilt가 언제 내 소스에 관련 Injection 소스들을 쑤셔넣어주냐 하면,

  • 구성요소 전체 기간

Hilt는 해당 Android 클래스의 수명 주기에 따라 생성된 구성요소 클래스의 인스턴스를 자동으로 만들고 제거합니다.

https://developer.android.com/training/dependency-injection/hilt-android#component-scopes

구글 공식사이트에서 이렇다고 한다.

참고

제일 자세한 한글 :https://hyperconnect.github.io/2020/07/28/android-dagger-hilt.html

dagger2가 궁금하다면 여기 : http://pluu.github.io/blog/android/2017/01/12/android-dagger/

0 comments:

댓글 쓰기