2021년 2월 1일 월요일

[Android] HILT 사용해보자.

 Spring 에서 서비스단의 클래스를 그냥 어노테이션으로 선언만하고 막쓰던것 처럼 DI해주는 Dagger 라는 라이브러리를 기반으로 안드로이드에서  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

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

ComponentScopeCreated atDestroyed atDefault Binidng
Application
Component
@SingletonApplication
#onCreate
Application
#onDestroy
Application
ActivityRetained
Component
@ActivityRetainedScopeActivity
#onCreate
Activity
#onDestroy
Application
Activity
Component
@ActivityScopedActivity
#onCreate
Activity
#onDestroy
Application,
Activity
Fragment
Componet
@FragmentScopedFragment
#onAttach
Fragment
#onDestroy
Application,
Activity,
Fragment
View
Component
@ViewScopedView
#super
View destroyedApplication,
Activity,
View
ViewWithFragment
Component
@ViewScopedView
#super
View destroyedApplication,
Activity,
Fragment,
View
Service
Component
@ServiceScopedService
#onCreate
Service
#onDestroy
Application,
Service

@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

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

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

3.간단하게 사용해보자

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
-------------------------------
//사용하고자 하는 클래스 변수에 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
    }
}

4.Module 간단한 사용.

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
--------------------------------
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}")

5.  같은 리턴타입의 프로바이더를 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}")


6. @bind로 사용자가 정의한 constructor를 써보자.

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
--------------------------------
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 클래스의 수명 주기에 따라 생성된 구성요소 클래스의 인스턴스를 자동으로 만들고 제거합니다.

생성된 구성요소생성 위치제거 위치
ApplicationComponentApplication#onCreate()Application#onDestroy()
ActivityRetainedComponentActivity#onCreate()Activity#onDestroy()
ActivityComponentActivity#onCreate()Activity#onDestroy()
FragmentComponentFragment#onAttach()Fragment#onDestroy()
ViewComponentView#super()제거된 뷰
ViewWithFragmentComponentView#super()제거된 뷰
ServiceComponentService#onCreate()Service#onDestroy()

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

참고 

제일 자세한 한글 :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:

댓글 쓰기