You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Kotlin Android中实现支持多ViewModel的通用ViewModelFactory?

精简统一ViewModelFactory的实现方案

以下两种方案都可以大幅减少现有代码的冗余逻辑:

方案1:无反射映射表实现(推荐)

这种方案不需要修改原有业务代码,仅对Factory内部的判断逻辑做结构化改造:

class AppViewModelFactory : ViewModelProvider.Factory {
    // 维护ViewModel类和实例创建逻辑的映射关系
    private val viewModelCreators = mapOf<Class<out ViewModel>, () -> ViewModel>(
        LoginViewModel::class.java to {
            LoginViewModel(LoginRepository(LoginDataSource()))
        },
        UserViewModel::class.java to {
            UserViewModel(UserRepository(UserDataSource()))
        },
        TransactionViewModel::class.java to {
            TransactionViewModel(TransactionRepository(TransactionDataSource()))
        }
    )

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        val creator = viewModelCreators[modelClass] 
            ?: viewModelCreators.entries.firstOrNull { modelClass.isAssignableFrom(it.key) }?.value
            ?: throw IllegalArgumentException("未找到匹配的ViewModel类:$modelClass")
        return creator() as T
    }
}

该方案的优势:

  • 无反射性能损耗,运行效率和原有if判断写法完全一致
  • 后续新增ViewModel仅需在viewModelCreators映射表中新增一行配置即可,无需新增if判断,代码结构更清晰
  • 支持任意构造参数的ViewModel,实例化逻辑和原有写法完全兼容

方案2:反射通用实现(适合构造规则统一的场景)

如果你的所有ViewModel、Repository、DataSource的构造参数规则完全统一(比如ViewModel仅接收对应Repository作为参数,Repository仅接收对应DataSource作为参数),可以用反射实现完全自动适配,后续新增ViewModel无需修改Factory代码:

class AppViewModelFactory : ViewModelProvider.Factory {
    @Suppress("UNCHECKED_CAST", "RemoveExplicitTypeArguments")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return try {
            // 取ViewModel的第一个构造函数
            val vmConstructor = modelClass.constructors.first()
            // 取构造函数第一个参数的类型(即对应Repository的类)
            val repoClazz = vmConstructor.parameterTypes[0]
            val repoConstructor = repoClazz.constructors.first()
            // 取Repository构造的第一个参数类型(即对应DataSource的类)
            val dsClazz = repoConstructor.parameterTypes[0]
            // 逐层实例化
            val dataSource = dsClazz.getDeclaredConstructor().newInstance()
            val repository = repoConstructor.newInstance(dataSource)
            vmConstructor.newInstance(repository) as T
        } catch (e: Exception) {
            throw IllegalArgumentException("ViewModel实例化失败:${modelClass.simpleName}", e)
        }
    }
}

注意事项:

  • 仅适配构造参数规则完全统一的场景,规则变动时需要同步修改反射逻辑
  • 存在少量反射开销,对性能要求极高的场景优先选择方案1

补充优化方案

如果项目已接入Hilt依赖注入框架,直接给ViewModel添加@HiltViewModel注解即可,无需手动实现ViewModelFactory,Hilt会自动生成全局统一的工厂实现,完全消除样板代码。


内容的提问来源于stack exchange,提问作者Kriyan Tech Zone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 09:42:01