본문 바로가기

빈 구멍 채우기

[삽질기록] error: Dagger does not support injecting maps of disallowed types: java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,dagger.internal.Provider<androidx.lifecycle.ViewModel>>

상황

Dagger로 ViewModel를 다루고 싶었다. 그러나 자꾸 해당 에러 메시지가 발생했다.

 

error: Dagger does not support injecting maps of disallowed types: java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,dagger.internal.Provider<androidx.lifecycle.ViewModel>>

 

Dagger version은 2.50

내가 짠 코드

참고한 포스트

https://medium.com/@Max_Sir/leveraging-dagger-for-viewmodel-dependency-injection-in-android-603c165731a8

 

Leveraging Dagger for ViewModel Dependency Injection in Android

Dependency injection (DI) is a fundamental technique in software engineering that enhances the modularity, flexibility, and testability of…

medium.com

 

 

내가 짠 코드와 비슷한 예제

class ViewModelFactory @Inject constructor(
    private val creators: @JvmSuppressWildcards Map<Class<out ViewModel>, Provider<ViewModel>>
) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        var creator: Provider<out ViewModel>? = creators[modelClass]
        if (creator == null) {
            for ((key, value) in creators) {
                if (modelClass.isAssignableFrom(key)) {
                    creator = value
                    break
                }
            }
        }
        if (creator == null) throw IllegalArgumentException("Unknown model class $modelClass")
        try {
            @Suppress("UNCHECKED_CAST")
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

@MustBeDocumented
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)

 

@Module
public abstract class ViewModelModule {

    @Binds
    abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory factory);
    
    @Binds
    @IntoMap
    @ViewModelKey(MyActivityViewModel.class)
    abstract ViewModel bindMyActivityViewModel(MyActivityViewModel viewModel);
}

 

앱 빌드 시 나오는 에러 메시지

 error: Dagger does not support injecting maps of disallowed types: java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,dagger.internal.Provider<androidx.lifecycle.ViewModel>>

 

ViewModelFactory에 주입되는 Map인 creator에서 자꾸 이슈가 생겼다.

 

도와줘 Gemini

(이미 ChatGPT와 실컷 삽질하고 찾은 Gemini)

 

제네릭 이슈였다.

 

마지막 삽질

Gemini가 알려준, 무식한 듯 하지만 선택지가 없어서 했던 작업. 수동으로 map을 생성해 주입하도록 짰다.

 

@Module
public abstract class ViewModelModule {

    @Binds
    abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory factory);

    // 추가한 부분
    // 아래 코드는 ViewModel이 많으면 번거롭지만, 오류를 우회할 수 있습니다.
    @Provides
    @Singleton // 또는 적절한 Scope
    static Map<Class<? extends ViewModel>, Provider<ViewModel>> provideViewModelCreators(
        // Dagger가 각 ViewModel에 대한 Provider를 주입해 줄 것입니다.
        Provider<MyActivityViewModel> myActivityViewModelProvider
        // 다른 ViewModel Provider들도 여기에 추가합니다. 예:
        // Provider<AnotherViewModel> anotherViewModelProvider
    ) {
        Map<Class<? extends ViewModel>, Provider<ViewModel>> creators = new HashMap<>();
        creators.put(MyActivityViewModel.class, (Provider<ViewModel>) myActivityViewModelProvider);
        // 다른 ViewModel들도 추가합니다. 예:
        // creators.put(AnotherViewModel.class, (Provider<ViewModel>) anotherViewModelProvider);
        return creators;
    }

    // 위에서 맵을 수동으로 제공하므로, 아래 @Binds @IntoMap 부분은 제거해야 합니다.
    // @Binds
    // @IntoMap
    // @ViewModelKey(MyActivityViewModel.class)
    // abstract ViewModel bindMyActivityViewModel(MyActivityViewModel viewModel);
}

 

그리고 나온 에러 메시지

error: Dagger disallows injecting the type: dagger.internal.Provider<com.dnt7.threeW.mypage.PremiumActivityViewModel>

 

도와줘 Gemini

 

즉, Provider<ViewModel> 대신 그냥 ViewModel을 쓰라는 이야기

 

해결

1. ViewModelModule은 원복

2. ViewModelFactory 수정

class ViewModelFactory @Inject constructor(
    private val creators: @JvmSuppressWildcards Map<Class<out ViewModel>, ViewModel>
) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        var creator: ViewModel? = creators[modelClass]
        if (creator == null) {
            for ((key, value) in creators) {
                if (modelClass.isAssignableFrom(key)) {
                    creator = value
                    break
                }
            }
        }
        if (creator == null) throw IllegalArgumentException("Unknown model class $modelClass")
        try {
            @Suppress("UNCHECKED_CAST")
            return creator as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

 

이게 된다!