[안드로이드스튜디오_문화][Hilt_바인딩 기법]

기말 지하기포·2024년 1월 13일
0

-Hilt에서 의미하는 바인딩이란 어떠한 의존성을 Hilt Component에 추가 하는 행위 또는 해당 의존성을 일컫는 용어를 의미한다.

바인딩하는 방법

@Inject(생성자)

-@Inject는 의존성을 요청할 때에만 사용되는 것이 아니라 생성자에 마킹함으로서 해당 객체에 대한 의존성을 주입받을 뿐만 아니라 바인딩 또한 함께 실행된다.

@Provides

-@Module이 마킹된 클래스 또는 object의 필드에 메서드를 선언한 이후에 @Provides를 마킹하면 Hilt Component에 메서드의 파라미터가 바인딩 된다.

-바인딩 되는 방식은 메서드의 반환값(Null 허용 X)이 바인딩 되는 형식으로 구성되어져 있다. Null을 허용하고 싶으면 BindsOptionalOf를 사용해야 한다.

@Binds

-기존에 바인딩 되어진 의존성이 존재할 때 새로운 @Provide 메서드를 만들지 않고 효율적으로 바인딩 할 수 있도록 해준다.

  • 예시코드(1)
  • 바인됭 것은 Engine이 아니라 GasolineEngine이다.
  • 만약, 클라이언트가 Hilt Component에게 Engine을 주입해달라고 요청한다면 컴파일 타임에서 Missing Binding 오류가 발생한다.
interface Engine

class GasolineEngine @Inject constructor() : Engine
  • 예시코드(2)
  • GasolineEngine을 Engine으로 사용하는 것이 가능하다.(다형성)
  • Binds의 파라미터에는 기존에 바인딩 된것을 명시해주면 된다.
  • 아래와 같이 작성한 이유는 반환타입을 캐스팅 가능한 상위 타입으로 선언하였다. (가솔린 엔진은 엔진으로 캐스팅이 가능해)
  • 그러면 Hilt-Component 내부에서는 이미 바인딩 된 GasolineEngine을 Engine으로 재바인딩하게 된다다.
@Module
@InstallIn(SingletonComponent::class)
abstract class EngineModule {
	
    @Binds
    abstract fun bindEngine(engine : GasolineEngine) : Engine
}

Binds 조건

-@Binds는 반드시 모듈 내부의 abstract 메서드에만 마킹이 가능하다.

-@Binds 메서드는 반드시 하나의 Parameter만 갖을 수 있다.

-Parameter 타입이 반환타입의 서브타입이여야 한다.

@Binds , @Provides 를 함께 사용 할 때 주의할 사항

  • 예시코드(1)
  • @Provides가 마킹된 메서드에 접근하고 호출하기 위해서는 반드시 객체가 있어야 한다. 하지만 @Binds가 마킹된 메서드는 abstract로 선언되었기 때문에 인스턴스를 생성할 수 없기 때문에 @Provides와 @Binds를 혼용해서 사용하면 문제가 생긴다.
  • 따라서 가능하다면 @Provides와 @Binds는 각각 독립된 모듈에 분리되어서 존재하는 것이 좋다.
  • 그런데 만약 같은 모듈에 둘 다 존재해야 한다면 companion object 키워드를 통해서 @Provides가 마킹된 메서드가 외부에서 접근이 가능하도록 만들어주면 된다. 그러면 MyModule을 인스턴스화 하지 않아도 MyModule.provideFoo()를 통해서 접근이 가능하다.
@Module
@InstallIn(SingletonComponent::class)
abstract class MyModule {

	companion object{
    	@Provides
        fun provideFoo() : Foo { }
    }
    
    @Binds
    abstract fun bindssEngine(engine:GasolineEngine) : Engine
}

@BindsOptionalOf

-바인딩이 확실히 되어있다는 보장이 없는 의존성을 요청할 때 사용한다.

-만약 Client가 Hilt-Component에 바인딩 되어있지 않은 의존성에 대한 주입을 요청한다면 컴파일 타임에 에러가 발생한다.

-그렇다면 Optional< T >를 클라이언트가 용청하면 컴파일 타임에서 에러가 발생하지 않도록 하는 방식을 사용 할 수 있다.

-Module 내부에서 @BindsOptionalOf를 설정하는 방법 예시코드 + 설명

  • @BindsOptionalOf는 abstract method에만 마킹이 가능하기 때문에 abstract class 필드 내부에서만 작성이 가능하다. @BindsOptionalOf가 마킹된 메서드의 리턴 타입으로 선택적 바인딩하고자 하는 타입을 선언하면 된다.
@Module
@InstallIn(SingletonComponent::class)
abstract class FooModule {
	
    @BindsOptionalOf
    abstract fun optionalFoo() : Foo
}

-@BindsOptionalOf가 마킹된 메서드를 통해 의존성을 주입받는 방법 예시코드 + 설명

  • 선택적으로 바인딩 된 의존성을 요청하고자 할 때에는 요청하고자 하는 타입을 제네릭 타입으로 갖는 타입을 요청하면 된다.
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
	
    @Inject
    lateinit var optionalFoo : Optional<Foo>
}


class Bar @Inject constructor(
	optionalFoo : Optional<Foo>
)


@Provides
fun provideString(
	optionalFoo : Optional<Foo>
) : String {

}

Optional< T >의 주요 메서드

-isPresent()

  • 바인딩 된 경우 true 반환

-get()

  • 바인딩 된 의존성 T를 반환핟다.
  • 바인징 되지 않은 경우 예외를 던진다.
  • orElse 같은 메서드 호출로 안전하게 접근할 수 있다.

@BindsInstance

-Hilt-Componet의 생성과 동사에 바인됭 된다.

멀티 바인딩하는 방법

-멀티 바인딩이 의미하는 것은 여러가지의 의존성을 하나의 컬렉션으로 관리하는 것을 의미한다.

-Collection 자체를 Hilt-Component에 바인딩하는 기법이라는 의미이다. Hilt에서는 Set과 Map을 통해서 MultiBinding을 구현 할 수 있다.

-멀티 바인딩을 사용해서 동일한 타입의 여러 객체들을 한 번에 관리하고 주입할 수 있다는 장점이 있다.

Set MultiBinding

-동일한 타입의 의존성들을 Set 형태로 관리하는 것을 의미한다.

@IntoSet

-@Provides가 마킹된 메서드에 @IntoSet을 마킹해서 멀키 바인딩을 적용 할 수 있다.

-@IntoSet을 추가하면 컴파일 타임에 Hilt-Component 내부에 Set을 하나 생성하고 Set의 제너릭 타입인 의존성들을 추가할 수 있는 환경을 만들어 준다.

  • 예시코드(1)
@Module
@InstallIn(SingletonComponent::class)
class MyModuleA {
	
    Provides
    @IntoSet
    fun provideOneString() : String {
    	return "ABC"
    }
}

@ElementsIntoSet

-요소들을 통째로 Set 타입 객체로 멀티바인딩 할 수 있다. @Provides가 마킹된 메서드에 @ElementsIntoSet을 마킹해서 멀티 바인딩을 적용 할 수 있다.

  • 예시코드(1)
@Module
@InstallIn(SingletonComponent::class)
class MyModuleB {
	
    @Provides
    @ElementsIntoSet
    fun provideTwoString() : Set<String> {
    	return listOf("DEF" , "GHI").toSet()
    }
}

멀티바인딩 된 Set 주입하는 방법

class Bar @Inject constructor(
	val strings : Set<String>
) {
	init{
    	assert(strings.contatins("ABC"))
        assert(strings.contatins("DEF"))
        assert(strings.contatins("GHI"))
    }
}

Map MultiBinding

-Map 자료구조는 키와 value 관계로 구성되어 있다. 따라서 Map을 사용한 멀티바인딩에서는 의존성과 관계를 맺을 key가 반드시 필요하다.

Map을 사용한 멀티바인딩을 위한 기본 키

-각각 상황에 맞게 알맞은 키를 선택해서 사용하면 된다.

-@StringKey

-@IntKey

-@LongKey

-@ClassKey

IntoMap

-바인딩 할 때 Key를 명시적으로 지정을 해야한다.

@Module
@InstallIn(SingletonComponent::class)
object MyModule {
	@Provides
    @IntoMap @StringKey("foo)
    fun provideFooValue() : Long {
    	return 100L
    }
    
    @Provides
    @IntoMap @ClassKey(Bar::class)
    fun provideBarValue() : String {
    	return "value for Bar"
    }
}

IntoMap을 사용해서 멀티바인딩 된 의존성을 주입하는 방법

-주입 할 때는 제너릭 타입으로 주입되므로 신경써서 주입해야 한다. 주입한 Map에 Key값을 넣어서 값을 가져올 수 있다.

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
	
    @Inject
    lateinit var map1 : Map<String , Long>
    
    @Inject
    lateinit var map2 : Map<Class< * > , String>
    
    override fun onCreate(savedInstanceState : Bundle?) {
    	super.onCreate(savedInstanceState)
        map1["foo"].toString
        map2[Bar::class.java]
    }
}

@MapKey

-@MapKey라는 어노테이션을 활요해서 커스텀 키를 만들어서 멀티바인딩에 사용 할 수 있다.

-1단계 : enum class로 키 모임 만들기

enum class MyEnum {
	ABC,
    DEF
}

-2단계 : @MapKey 어노테이션을 사용해서 위에서 만든 enum class를 키로 활용 할 수 있도록 커스텀한 어노테이션을 만든다.

@MapKey
annotation class MyEnumKey(val value : MyEnum)

-3단계 : 멀티 바인딩하기

@Module
@InstallIn(SingletonComponent::class)
object MyModule {
	
    @Provides
    @IntoMAp
    @MyEnumKey(MyEnum.ABC)
    fun provideABCValue() : String {
    	return "value for ABC"
    }
    
    
    @Provides
    @IntoMAp
    @MyEnumKey(MyEnum.DEF)
    fun provideABCValue() : String {
    	return "value for DEF"
    }
}

-4단계 : 의존성 주입받기
-주입 할 때는 제너릭 타입으로 주입되므로 신경써서 주입해야 한다.주입한 Map에 enum class에서 만든 Key값을 넣어서 값을 가져올 수 있다.

class MainActivity : ComponentActivity() {

	@Inject
    lateinit var map : Map<MyEnum , String>
    
    override fun onCreate(savedInstanceState : Bundle) {
    	super.onCreate(savedInstanceState)
        map[MyEnum.ABC]
        map[MyEnum.DEF]
    }
}
profile
포기하지 말기

0개의 댓글