最佳的ViewModel注入方式是什么?

3

我过去曾经使用过koin,在koin中使用单行代码即可注入viewModel。现在我需要知道如何在没有koin的情况下实现这一操作! 在ViewModelFactory中,应该使用一个大的switch/case语句为不同的viewmodels创建实例吗?

class ViewModelFactory: ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
       return when(modelClass) {
            is FirstViewModel -> FirstViewModel()
            is SecondViewModel -> SecondViewModel()
                ...
        }
    }
}

但是,我必须将所有viewmodel的依赖项注入到工厂中。这样的代码真的很混乱。即使没有这个问题,switch/case本身也很混乱!在大型项目中特别不建议这样做。那么有没有其他替代方法呢?
Dagger能如何帮助解决这个问题?


使用Hilt注入ViewModel同样也很简单。你正在使用哪个DI系统? - ianhanniballake
@ianhanniballake,当使用dagger注入ViewModelStateOwner时,有什么推荐吗? - undefined
1个回答

4

实际上,有很好的替代方案。

首先:人们往往会忘记的第一个方法是为每个ViewModel创建一个ViewModelfactory,这比创建一个庞大的工厂要好得多。这就是我们所说的单一责任原则。

class FirstViewModelFactory(val firstDependency: SomeClass, val secondDependency: SomeOtherClass): ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        FirsViewModel(firstDependency, secondDependency) as T
    }
}

在活动中:

val viewModel: FirstViewModel = ViewModelProvider(this, FirstViewModelFactory(first, second))[FirstViewModel::class.java]

第二点:如果您希望为所有ViewModel只使用一个工厂,则可以定义一个接受lambda表达式的通用工厂:

class ViewModelFactory<VM: ViewModel>(val provider: () -> VM): ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return  provider() as T
    }
}

并在活动或片段中像这样使用它:

val ViewModel: FirstViewModel = ViewModelProvider(this, ViewModelFactory<FirstViewModel>{
    FirstViewModel(first, second)
})[FirstViewModel::class.java]

这实际上使你的生活变得更轻松。但仍然可以进一步改进。

第三点:你可以告诉Dagger如何为你提供FirstViewModel及其依赖项。 如果你不知道如何使用Dagger,请先学习它,然后再阅读本部分。 你需要告诉Dagger你想要获取一个FirstViewModel的实例。这个位置在AppComponent中。

@Component(modules = [AppModule::class])
interface AppComponent {
    val applicationContext: Context
    val firstViewModel: FirstViewModel
    
    ...

    @Component.Factory
    interface Factory {
        fun create(@BindsInstance applicationContext: Context): AppComponent
    }
}

在你的appModule中,你需要使用@Provides注解告诉dagger如何提供FirstViewModel的依赖项。然后你需要在应用程序类中实例化你的dagger组件,有很多方法可以做到这一点,我只是使用了工厂接口:

class MyApplication: Application() {
    val component: AppComponent by lazy {
        DaggerAppComponent.factory().create(applicationContext)
    }
}

不要忘记在清单文件中添加MyApplication

然后,在您的活动中,您可以注入viewModel而无需担心依赖关系:

val appComponent = (application as MyApplication).appComponent

val viewModel: FirstViewModel = ViewModelProvider(this, ViewModelFactory<FirstViewModel>{
    appComponent.firstViewModel
})[FirstViewModel::class.java]

您可以使用lazy和扩展函数使其更美观易读,最终可以得到以下类似代码:

private val viewModel: FirstViewModel by injectVmWith { appInjector.firstViewModel }

第四种方式:您可以始终使用Dagger的multibinding。使用此方法,您将ViewModelFactory注入到活动或片段中,并从其中检索viewModel。通过这种方式,您告诉dagger将所有viewModels放入一个映射中,并将其注入到ViewModelFactory中。通过对它们进行注释,您帮助dagger找到viewModels。还使用另一个注释告诉dagger它们的键是什么。在另一个模块中执行所有操作,并且对于您需要的每个viewModel,您的viewModel模块中都需要一个函数。然后,在庞大的工厂中,您可以告诉dagger根据其类型从映射中获取所需的viewModel,而不是使用switch/case。这是一种服务定位器(反?)模式。这是另一个话题,本答案已经太长了。请参考这篇文章这篇文章

总结:我认为如果您正在使用dagger,则第三种方式肯定是赢家。multibinding也很好,但每次添加一个viewmodel时,您都需要记住在viewmodelModule中添加一个函数(就像Koin一样)。如果您没有使用dagger,则在我看来,第二种方法是最好的,但您应该决定哪种方式最适合您。Koin也很棒!


嘿,你能在这里帮忙吗?https://dev59.com/w73pa4cB1Zd3GeqPlMcC - IRON MAN

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接