通过视图模型和使用工厂创建视图模型之间的区别?

yby*_*byb 6 android kotlin android-mvvm android-viewmodel

我正在研究ViewModel将其应用到MVVM设计模式中。

有一个使用方法by viemodels()和一个ViewModelProvider.Factory在视图模型创建中使用的方法。

by viewModels()创建一个ViewModel object.

ViewModelProvider.Factory还创建Viewmodel objects.

这两者有什么区别?

另外,在一些示例代码中,我看到注释中的代码3,其中使用了by viewModels()and factory。这是什么意思?

class WritingRoutineFragment : Fragment() {
    private val viewModel: WriteRoutineViewModel by viewModels() // 1
    private lateinit var viewModelFactory: WriteRoutineViewModelFactory

//  private val viewModel: WriteRoutineViewModel by viewModels(
//        factoryProducer = { viewModelFactory } // 3.What does this code mean?
//  )

    override fun onCreateView(inflater: LayoutInflater,
                              container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        _binding = FragmentWritingRoutineBinding.inflate(inflater, container, false)
        viewModelFactory = WriteRoutineViewModelFactory()
//        viewModel = ViewModelProvider(this, viewModelFactory).get(WriteRoutineViewModel::class.java) // 2
        return binding.root
    }
Run Code Online (Sandbox Code Playgroud)

Ten*_*r04 18

如果您的 ViewModel 有一个零参数构造函数,或者它的构造函数的唯一参数是 Application 类型并且它是 AndroidViewModel 的子类,那么您不需要工厂。(或者,如果您的构造函数是上述任一加上 SavedStateHandle。)视图模型工厂是一个能够实例化具有更复杂构造函数的 ViewModel 的类。

在不使用委托的情况下实例化 ViewModel 时,您必须使用 alateinit var作为属性,因为在 之前无法实例化它 onCreateView

如果您的 ViewModel 不需要工厂,那么在没有委托的情况下执行此操作的过程将如下所示:

class WritingRoutineFragment : Fragment() {
    private lateinit var viewModel: WriteRoutineViewModel

    override fun onCreateView(inflater: LayoutInflater,
                              container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        //...
        viewModel = ViewModelProvider(this, viewModelFactory).get(WriteRoutineViewModel::class.java)
        //...
    }
}
Run Code Online (Sandbox Code Playgroud)

如果它确实需要一个工厂,它看起来像这样,您必须实例化一个工厂并将其传递给 ViewModelProvider 构造函数:

class WritingRoutineFragment : Fragment() {
    private lateinit var viewModel: WriteRoutineViewModel

    override fun onCreateView(inflater: LayoutInflater,
                              container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        //...
        viewModel = ViewModelProvider(this, WriteRoutineViewModelFactory()).get(WriteRoutineViewModel::class.java)
        //...
    }
}
Run Code Online (Sandbox Code Playgroud)

委托允许您在val声明站点的右侧更简洁地执行此操作,因此您不必在onCreateView. 第一次使用该属性时,它将延迟创建 ViewModel。优点是代码更简洁、更清晰(lateinit var将属性与其声明分开,并使其可变,即使它永远不会改变)。

因此,当不需要工厂时,上面的代码如下所示:

class WritingRoutineFragment : Fragment() {
    private val viewModel: WriteRoutineViewModel by viewModels()
}
Run Code Online (Sandbox Code Playgroud)

如果你确实需要一个工厂,它会是这样的。您向它传递一个实例化工厂的函数,这可以使用 lambda 轻松完成:

class WritingRoutineFragment : Fragment() {
    private val viewModel: WriteRoutineViewModel by viewModels { WriteRoutineViewModelFactory() }
}
Run Code Online (Sandbox Code Playgroud)

您的示例中的代码有一个额外的属性只是为了保存工厂,这是一个不必要的复杂化,因为您永远不需要直接访问它。同样很奇怪的是,示例中的工厂有一个空构造函数,因为如果工厂没有任何状态,则它没有数据可传递给 ViewModel 构造函数。

  • 添加第一点:您还可以使用默认工厂来处理使用 [`SavedStateHandle`](https://developer.android.com/topic/) 的一个参数 `ViewModel`(或两个参数 `AndroidViewModel`)库/体系结构/viewmodel-savedstate)。 (2认同)