[Android/DI] Dagger2 - MultiBinding 주입하기

2021. 6. 29. 15:45·Android/Dependency Injection
반응형

Dagger에는 단일 데이터를 주입받는 방법 외에도, 자료구조를 이용하여 n개의 데이터를 주입받는 방법이 있습니다. 대표적으로 Set과 Map을 이용하는 방법이 존재합니다.

Set MultiBinding

먼저, Set을 이용한 MultiBinding입니다.

SetModule.kt

@Module
class SetModule {

    @Provides
    @IntoSet
    fun provideHello(): String = "Hello"

    @Provides
    @IntoSet
    fun provideWorld(): String = "World"

    @Provides
    @ElementsIntoSet
    fun provideSet(): Set<String> {
        return HashSet(listOf("seosh", "817"))
    }
}

@IntoSet을 이용하여 하나씩 넣는 방법과 @ElementsIntoSet을 이용하여 한번에 넣어주는 방법이 있습니다.

"Hello", "World"는 @IntoSet으로,@ElementsIntoSet은 "seosh", "817"을 모듈에 넣어주도록 하겠습니다.

MultiBindingComponent.kt

@Component(modules = [SetModule::class])
interface MultiBindingComponent {
    fun inject(multiBindingComputer: MultiBindingComputer)
}

그리고 난 뒤, Component에서 MultiBindingComputer의 injection Method를 작성해 줍니다.

MultiBindingComputer.kt

import javax.inject.Inject

class MultiBindingComputer {

    @Inject
    lateinit var stringSet: Set<String>


    fun printSets() {
        val itr = stringSet.iterator()
        while(itr.hasNext()) {
            println(itr.next())
        }
    }
}

주입받은 Set 데이터를 print해주도록 하겠습니다.

실행결과

Set을 모두 출력해보면 주입해주었던 String들이 데이터들이 모두 출력되는 결과를 확인하실 수 있습니다.

Map MultiBinding

Map MultiBinding에서 사용되는 기본적인 Key는 StringKey, IntKey, LongKey, ClassKey 가 존재합니다.

@IntoMap 어노테이션과 같이 사용해 주면 Module에서 각각의 Key와 Value들이 저장되어 주입받을 수 있습니다.

 

가장 중요한 Key는 @MapKey를 이용하여 어노테이션을 생성한 후 SubClass 혹은 EnumClass를 Key로 사용하는 방법입니다.

@MapKey를 이용하면 어떠한 복잡한 클래스던지 MultiBinding으로 반환되는 Map의 Key로 사용하여 원하는 결과를 얻어낼 수 있습니다.

 

예제에서는 기본적인 Key들과 @MapKey를 이용한 Custom Map MultiBinding을 다루어보겠습니다. 

 

기본적인  Key를 이용한 Map MultiBinding

Ram.kt

interface Ram {
    val name: String
    val company: String
}

class Ram8G @Inject constructor(): Ram {

    override val name: String
        get() = Name

    override val company: String
        get() = "Hynix"

    companion object {
        const val Name ="8G RAM"
    }
}

class Ram16G : Ram {

    override val name: String
        get() = Name

    override val company: String
        get() = "Samsung"

    companion object {
        const val Name ="16G RAM"
    }
}

MapModule.kt

@Module
class MapModule {

    @Provides
    @IntoMap
    @StringKey("name")
    fun provideStringName() : String = "seosh"

    @Provides
    @IntoMap
    @IntKey(817)
    fun provideIntToName() : String = "seosh"

    @Provides
    @IntoMap
    @ClassKey(Ram::class)
    fun provideRam() : Ram = Ram8G()
}

@StringKey, @IntKey, @ClassKey안에 각각 Key들을 넣어줍니다.

Key: "name", Value: "seosh"

Key: 817, Value: "seosh"

Key: Ram, Value: Ram8G

로 넣어주었습니다.

@Component(modules = [MapModule::class])
interface MultiBindingComponent {

    // Map MultiBinding

    fun getStringKeyMap(): Map<String, String>

    fun getIntKeyMap(): Map<Int, String>

    fun getClassKeyMap(): Map<Class<*>, Ram>

    @Component.Builder
    interface Builder {
        fun mapModule(mapModule: MapModule): Builder
        fun build(): MultiBindingComponent
    }
}

TestCode

@Config(manifest = Config.NONE, sdk = [28])
@RunWith(RobolectricTestRunner::class)
class ComputerUnitTest {
    @Test
    fun testMultiBindingMap() {
        val multiBindingComponent: MultiBindingComponent = DaggerMultiBindingComponent.create()

        // Map MultiBinding
        println(multiBindingComponent.getStringKeyMap()["name"])
        println(multiBindingComponent.getIntKeyMap()[817])
        println(multiBindingComponent.getClassKeyMap()[Ram::class.java]?.name)
    }
}

결과를 확인하기 위해 넣어준 key를 순서대로 출력해보면 원하는 결과대로 출력되는 것을 확인하실 수 있습니다.

 

Set MultiBinding Test

 

@MapKey를 이용한 CustomKey MultiBinding

@MapKey를 이용하여 어노테이션을 생성한 후 SubClass 혹은 EnumClass를 Key로 사용할 수 있습니다.

Annotations.kt

enum class MyInfoEnum {
    FirstName, LastName,
}

@MapKey
annotation class RamClassKey(val value: KClass<out Ram>)

@MapKey
annotation class MyEnumKey(val value: MyInfoEnum)

 

 

어노테이션 클래스에 @MapKey 어노테이션을 달아주고 Ram클래스의 공변으로 인자로 받을 수 있도록 만듭니다.

이렇게 넣어주면 Ram의 SubClass를 Key로 사용할 수 있게 됩니다.

 

또한, @MapKey는 Enum Class도 인자로 받을 수 있습니다.

 

MapModule.kt

@Module
class MapModule {

    @Provides
    @IntoMap
    @RamClassKey(Ram8G::class)
    fun provideRam8G() : Ram = Ram8G()

    @Provides
    @IntoMap
    @RamClassKey(Ram16G::class)
    fun provideRam16G() : Ram = Ram16G()

    @Provides
    @IntoMap
    @MyEnumKey(MyInfoEnum.LastName)
    fun provideInfoLastName(): String = "SH"

    @Provides
    @IntoMap
    @MyEnumKey(MyInfoEnum.FirstName)
    fun provideInfoFirstName(): String = "Seo"
}

위에서 선언한 어노테이션을 이용하여 @IntoMap 어노테이션과 함께 호출하여 Key와 Value를 지정해줍니다.

@Component(modules = [MapModule::class])
interface MultiBindingComponent {

    // CustomKey MultiBinding

    fun getCustomKeyMap(): Map<MyInfoEnum, String>

    fun getRamClassKeyMap(): Map<Class<out Ram>, @JvmSuppressWildcards Ram>

    @Component.Builder
    interface Builder {
        fun mapModule(mapModule: MapModule): Builder
        fun build(): MultiBindingComponent
    }
}

Component에서 Injection Method를 작성해줍니다.

TestCode

@Config(manifest = Config.NONE, sdk = [28])
@RunWith(RobolectricTestRunner::class)
class ComputerUnitTest {

    @Test
    fun testMultiBindingMap() {
        val multiBindingComponent: MultiBindingComponent = DaggerMultiBindingComponent.create()

        //CustomKey MultiBinding
        println(multiBindingComponent.getRamClassKeyMap()[Ram8G::class.java]?.name)
        println(multiBindingComponent.getRamClassKeyMap()[Ram16G::class.java]?.name)
        println(multiBindingComponent.getCustomKeyMap()[MyInfoEnum.FirstName])
        println(multiBindingComponent.getCustomKeyMap()[MyInfoEnum.LastName])

    }
}

아래 결과를 통해 원하는 결과대로 나온 것을 확인하실 수 있습니다.

 

Map MultiBinding Test

반응형
'Android/Dependency Injection' 카테고리의 다른 글
  • [Android/DI] Dagger2 - Scope와 dagger.android
  • [Android/DI] Dagger2 - Qualifier 어노테이션
  • [Android/DI] Dagger2 - Lazy Injection vs Provider Injection
  • [Android/DI] Dagger2 - 소개 및 Module과 Component
seunghwaan
seunghwaan
공부한 내용을 정리하는 개발 기록 블로그
  • seunghwaan
    SH's Devlog
    seunghwaan
  • 전체
    오늘
    어제
    • 분류 전체보기 (144)
      • Android (62)
        • Basic (17)
        • Kotlin(Java) (14)
        • UI & Animation (1)
        • Compose (2)
        • Coroutines (1)
        • Dependency Injection (6)
        • RxJava (8)
        • BLE (3)
        • TDD (2)
        • JetPack (1)
        • NextStep (4)
        • Error Log (3)
      • Flutter (14)
        • Basic (5)
        • Dart (1)
        • State Management (2)
        • Widgets (4)
        • Error and Tips (2)
      • CS(Computer Science) (18)
        • Network (4)
        • Database (10)
        • Design Pattern (1)
        • Computer Architecture (3)
        • Operating System (0)
      • iOS (8)
        • Basic (0)
        • Swift (8)
      • Cloud (6)
        • AWS (6)
      • Web Frontend (0)
        • JavaScript(TS) (0)
        • React (0)
      • DevOps (25)
        • GIT (4)
        • CI CD (8)
        • Linux (4)
        • Docker (9)
        • Error Log (0)
      • 코딩테스트 (10)
        • DB (6)
        • 알고리즘 (4)
      • Backend (1)
        • Spring (1)
      • Mac Tip (0)
      • Temporary (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    IOS
    di
    FLUTTER
    error
    네트워크
    cognito
    cd
    Kotlin
    Dependency Injection
    database
    RxJava
    Jenkins
    Dagger
    상태 관리
    gradle
    Swift
    BLE
    Android
    docker
    컴퓨터공학
    cs
    Linux
    Algorithm
    MySQL
    AWS
    시작하세요! 도커
    CICD
    Network
    Computer Science
    CI
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
seunghwaan
[Android/DI] Dagger2 - MultiBinding 주입하기
상단으로

티스토리툴바