12. 멀티 바인딩 하기
- dagger의 멀티 바인딩을 사용하면 여러 모듈에 있는 같은 타입의 객체를 하나의 set, map 형태로 관리 가능
Set 멀티 바인딩
@IntoSet
,@ElementsIntoSet
->@Provides
예제 코드
import dagger.Module
import dagger.Provides
import dagger.multibindings.ElementsIntoSet
import dagger.multibindings.IntoSet
@Module
class SetModule {
@Provides
@IntoSet
fun provideHello() = "Hello"
@Provides
@IntoSet
fun provideWorld() = "World"
@Provides
@ElementsIntoSet
fun provideSet() = setOf<String>("Lee", "Kim")// set 일부를 한번에 추가
}
import dagger.Component
@Component(modules = [SetModule::class])
interface SetComponent {
fun inject(foo: Foo)
}
import javax.inject.Inject
class Foo {
@Inject
lateinit var strings: Set<String>
fun print() {
for (item in strings) {
println(item)
}
}
}
테스트 코드
@Test
fun test_multiBinding(){
val foo = Foo()
DaggerSetComponent.create().inject(foo)
foo.print()
}
출력
Hello
World
Lee
Kim
Map 멀티 바인딩
- 모듈 내부에
@Provides
메서드에@IntoMap
추가 - 주의사항 : Map 사용 시 Key 필요하므로. 별도 키 어노테이션이 필요함
기본 제공 키 종류
@StringKey
@ClassKey
@IntKey
@LongKey
예시 코드
class Foo {
}
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import dagger.multibindings.StringKey
@Module
class MapModule {
companion object {
@Provides
@IntoMap
@StringKey("foo")
fun provideFooValue() = 100L
@Provides
@IntoMap
@ClassKey(Foo::class)
fun provideFooStr() = "Foo String"
}
}
import dagger.Component
@Component(modules = [MapModule::class])
interface MapComponent {
fun getLongByString(): Map<String, Long>
fun getStringByClass(): Map<Class<*>, String>
}
테스트 코드
@Test
fun test_multiBindingMap(){
val component = DaggerMapComponent.create()
val value = component.getLongByString()["foo"]
val str = component.getStringByClass()[Foo::class.java]
println(value)
println(str)
}
100
Foo String
사용자 정의 키 만들기
@MapKey
어노테이션 사용
AnimalKey, NumberKey를 사용하여 멀티 바인드 하는 예제
enum class Animal {
CAT, DOG
}
import dagger.MapKey
@MapKey
annotation class AnimalKey(val value: Animal)
import dagger.MapKey
import kotlin.reflect.KClass
@MapKey
annotation class NumberKey(val value: KClass<out Number>) // Number 클래스의 서브 클래스 타입을 속성으로.
import dagger.Component
@Component(modules = [MapModule::class])
interface MapKeyComponent {
fun getStringsByAnimal(): Map<Animal, String>
fun getStringsByNumber(): Map<Class<out Number>, String>
}
반응형
import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoMap
@Module
class MapModule {
@IntoMap
@AnimalKey(Animal.CAT)
@Provides
fun provideCat() = "Meow"
@IntoMap
@AnimalKey(Animal.DOG)
@Provides
fun provideDog() = "Bow-wow"
@IntoMap
@NumberKey(Float::class)
@Provides
fun provideFloatValue() = "100f"
@IntoMap
@NumberKey(Int::class)
@Provides
fun provideIntValue() = "1"
}
테스트 코드
@Test
fun test_customMapKey(){
val component = DaggerMapKeyComponent.create()
val cat = component.getStringsByAnimal()[Animal.CAT]
val dog = component.getStringsByAnimal()[Animal.DOG]
val number = component.getStringsByNumber()[Float::class.java]
println(cat)
println(dog)
println(number)
}
출력
Meow
Bow-wow
100f
상속된 서브 컴포넌트의 멀티 바인딩
- 바인드된 set, map을 서브 컴포넌트도 그대로 물려 받을 수 있음
예제 코드
import dagger.Component
@Component(modules = [ParentModule::class])
interface ParentComponent {
fun strings():Set<String>
fun childCompBuilder(): ChildComponent.Builder
}
import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoSet
@Module(subcomponents = [ChildComponent::class])
class ParentModule {
@Provides
@IntoSet
fun string1() = "parent string 1"
@Provides
@IntoSet
fun string2() = "parent string 2"
}
import dagger.Subcomponent
@Subcomponent(modules = [ChildModule::class])
interface ChildComponent {
fun strings(): Set<String>
@Subcomponent.Builder
interface Builder{
fun build():ChildComponent
}
}
import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoSet
@Module
class ChildModule {
@Provides
@IntoSet
fun string3() = "child string 1"
@Provides @IntoSet
fun string4() = "child string 2"
}
테스트 코드
@Test
fun test_multiBindingSubcomponent(){
val parentComp = DaggerParentComponent.create()
val childComp = parentComp.childCompBuilder().build()
println("List set in Parent")
var itr = parentComp.strings().iterator()
while(itr.hasNext()){
println(itr.next())
}
println("List set in Child")
itr = childComp.strings().iterator()
while(itr.hasNext()){
println(itr.next())
}
}
출력
List set in Parent
parent string 1
parent string 2
List set in Child
child string 2
child string 1
parent string 1
parent string 2
추상적인 멀티 바인딩 선언하기
- 컴포넌트는 여러 모듈을 사용할 수 있는데 다른 모듈에 의해서 멀티 바인드를 사용할 수도 있고 안할 수도 있다.
- 멀티 바인딩 사용 여부와 상관 없이
@Multibinds
어노테이션으로 멀티 바인딩 선언 가능 - 매개 변수를 갖지 않는 추상 메서드에 사용 가능, 반환 타입은 Map 이나 Set
import dagger.Component
@Component(modules = [MultibindsModules::class])
interface MultibindsComponent {
fun getStrings(): Set<String>
}
import dagger.Module
import dagger.multibindings.Multibinds
@Module
abstract class MultibindsModules {
@Multibinds
abstract fun strings(): Set<String>
}
테스트
@Test
fun test_multiBinds(){
val component = DaggerMultibindsComponent.create()
// 비어 있음
for(s in component.getStrings()){
println(s)
}
}
- 비어있기 때문에 아무 출력 없음
- 비어 있는 Set을 멀티 바인딩한 효과와 같아
@ElementsIntoSet
만 사용해서 멀티 바인딩일 선언할 수도 있음
@Module
abstract class MultibindsModules {
companion object{
@Provides
@ElementsIntoSet
fun emptyStrings() = Collections.emptySet<String>()
}
}
2021.07.26 - [Android/클린 아키텍처] - [Clean Architecture] 12-바인딩의 종류 (Dagger)
2021.07.25 - [Android/클린 아키텍처] - [Clean Architecture] 11-범위 지정하기 (@Scope)
2021.07.24 - [Android/클린 아키텍처] - [Clean Architecture] 10-한정자 (@named)
해당 글은 '아키텍처를 알아야 앱 개발이 보인다' 를 공부하며 요약 정리한 글 입니다.
'Android > 클린 아키텍처' 카테고리의 다른 글
[Clean Architecture] 15-안드로이드에서의 Dagger2 (0) | 2021.08.04 |
---|---|
[Clean Architecture] 14-Dagger 서브 컴포넌트, 상속 (0) | 2021.08.03 |
[Clean Architecture] 12-바인딩의 종류 (Dagger) (0) | 2021.07.26 |
[Clean Architecture] 11-범위 지정하기 (@Scope) (0) | 2021.07.25 |
[Clean Architecture] 10-한정자 (@named) (0) | 2021.07.24 |