티스토리 뷰

ViewModel 자동 DI

1. 키생성

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

다음과 같이 ViewModelKey 어노테이션 클래스를 생성한다. 이 어노테이션을 ViewModel에 달아주면 해당 ViewModel이 밑의 코드에 있는 viewModelProviders에 저장할 키 값임을 명시하는 것이다.

2. 커스텀 ViewModelFactory생성

@ContributesBinding(AppScope::class)
class MetroViewModelFactory @Inject constructor(
    private val creators: Map<KClass<out ViewModel>, Provider<ViewModel>>,
    ) : ViewModelProvider.Factory {
    @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
    override fun <T : ViewModel> create(
        modelClass: Class<T>,
        extras: CreationExtras,
    ): T {
        val provider = creators[modelClass.kotlin] ?: error("$modelClass")
        return modelClass.cast(provider())
    }
}

위의 코드와 같이 MetroViewModelFactory 객체에서 viewModelProviders에 값을 넣어줄 수 있다.

creators는 Metro가 자동으로 생성하여 주입해주기 때문에 따로 그래프에 정의를 하거나 어노테이션을 통해 등록해주지 않아도 된다.

@ContributesIntoMap(AppScope::class)
@ViewModelKey(SettingViewModel::class)
class SettingViewModel @Inject constructor(
    private val festivalNotificationRepository: FestivalNotificationRepository,
) : ViewModel() {
...
}

@ContributesIntoMap을 사용하여 해당 스코프 안에서, Map에 저장할 수 있도록 하며 위에서 언급했던 것 처럼 @ViewModelKey가 ViewModel을 찾는 키 역할을 하게 된다.

 

@ContributesIntoMap(
    scope = AppScope::class,
    binding = binding<Fragment>(),
)
@FragmentKey(SettingFragment::class)
@Inject
class SettingFragment(
    private val notificationPermissionManagerFactory: NotificationPermissionManager.Factory,
) : BaseFragment<FragmentSettingBinding>(),
    NotificationPermissionRequester {
    override val layoutId: Int = R.layout.fragment_setting

    override val defaultViewModelProviderFactory: ViewModelProvider.Factory
        get() = appGraph.metroViewModelFactory
    
    private val settingViewModel: SettingViewModel by viewModels({ requireActivity() })

defaultViewModelProviderFactory를 오버라이딩해서 커스텀한 MetroViewModelFactory와 연결해주면 by viewModel()을 통해 뷰모델 객체를 생성할 때 자동으로 MetroViewModelFactory를 통해 viewModel 객체를 생성해준다.

수동 ViewModel 생성

하지만 @Assisted, @AssistedFactory을 ViewModel의 생성자 파라미터로 사용할 경우는viewModelProviders에 넣어줄 수 없다.

class ScheduleViewModel @AssistedInject constructor(
    private val scheduleRepository: ScheduleRepository,
    @Assisted private val dateId: Long,
) : ViewModel() {
    @AssistedFactory
    interface Factory {
        fun create(dateId: Long): ScheduleViewModel
    }
    ...
 }

따라서 다음과 같이 Factory 메서드를 직접 구현 해준 후, 수동으로 생성해주어야한다.

 companion object {
        const val INVALID_ID: Long = -1L

        fun factory(
            factory: Factory,
            dateId: Long = INVALID_ID
        ): ViewModelProvider.Factory =
            viewModelFactory {
                initializer {
                    factory.create(dateId)
                }
            }
    }
private val viewModel: ScheduleViewModel by viewModels {
        val dateId: Long = arguments?.getLong(KEY_DATE_ID, INVALID_ID) ?: INVALID_ID
        ScheduleViewModel.factory(appGraph.scheduleViewModelFactory, dateId)
    }

 

모르는게 아직 많지만 일단 정리해보았다. 나중에 다시 열어보면 부끄러운 코드가 될 듯하다.

'안드로이드' 카테고리의 다른 글

LifeCycle Of View  (0) 2025.12.09
Hilt, Metro의 DI 그래프 생성, 등록, 주입 차이  (0) 2025.12.08
Lottie  (0) 2025.11.05
LifeCycle Of ViewModel  (1) 2025.10.05
Android Thread 통신과 Handler·Looper  (4) 2025.08.29
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/01   »
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
글 보관함