Android 创建 viewModel 对象的不同方法在什么时候使用哪一种?

Android different ways to create viewModel object which one to use when?

我最近开始使用 ViewModel 和 AndroidViewModel,我看到有不同的方法来初始化 viewModel 实例,对我来说都很好,我只是想知道什么时候使用哪个?我应该在哪里初始化 viewModel 对象?以下是获取 viewModel 实例并为我工作的不同方法:

    val myViewModel1 = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(MyViewModel::class.java)
    val myViewModel2 = ViewModelProvider.AndroidViewModelFactory(this.application).create(MyViewModel::class.java)
    val myViewModel3 = ViewModelProvider(this).get(MyViewModel::class.java)
    val myViewModel4: MyViewModel by viewModels()
    val myViewModel5 by viewModels<MyViewModel>()

对我来说最简单最简单的是第3、4和5种方法,但是我不知道这五种方法有什么区别,如果还有其他方法或最佳初始化方法,请告诉我我的 viewModel 对象,我在声明它时对全局变量进行了初始化,可以在声明时初始化还是应该在某些生命周期方法中完成?

3 是获取(并在必要时创建)没有构造函数参数的 ViewModel 的标准方法。在内部,这是在做 1 来传递调用空构造函数的工厂 (NewInstanceFactory())。

AndroidViewModelViewModel 的子类,它会自动传入 application 引用,以防您需要访问应用程序上下文等内容。所以即使你的AndroidViewModel没有参数,创建它的工厂也需要传入一个application,这就是2所做的。

默认情况下,使用 3 即可为您解决所有问题 - 如果您的 VM 需要配置一些额外参数,您只需定义和使用工厂。


45 是一回事,只是在不同的地方指定了类型(你只需要一个声明和其他将被推断)。它们是 KTX 库的代表,它们做的事情与 3 相同,但在我看来它们的可读性要高得多——尤其是当你混合作用域时,比如使用 [=15] =] 获取片段自己的 VM,以及 by activityViewModels 获取 Activity 的 VM 以与该片段和其他片段共享数据。

它们也是 lazy 委托(据我所知!)这意味着 VM 仅在首次访问时才被实例化,这通常会在生命周期的后期发生(而不是何时该对象首先被构造)。我不确定 在构造时初始化 VM 的问题,但我看到的所有官方示例似乎都在 onCreate (或附近)中获取它

如果有人在寻找深入的答案,请检查这个,这里我们有以下创建或获取 viewModel 对象的方法:

  1. val myViewModel1 = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(MyViewModel::class.java)

  2. myViewModel2 = ViewModelProvider.AndroidViewModelFactory(this.application).create(MyViewModel::class.java)

  3. val myViewModel3 = ViewModelProvider(this).get(MyViewModel::class.java)

  4. val myViewModel4: MyViewModel by viewModels()

  5. val myViewModel5 by viewModels<MyViewModel>()

都做同样的事情,唯一的两个主要区别是:

  1. 使用延迟加载和不使用延迟加载的 viewModel 初始化。
  2. 多参数和无参数的viewModel

让我们看看 lazy loading and without lazy loading,前三个没有委托 by,这意味着该对象没有延迟加载,所以它是开发人员 仅当创建 activity 或片段附加到 activity 时才负责创建 viewModel 对象,这意味着前三种方法(1、2、3)不能 在全局范围内使用,如果在全局范围内使用,则变量必须是 具有 lateint 或 null 初始化的 var,以及 初始化(方法 1、2、3)必须发生在 onCreate 或 onViewCreated(在片段的情况下)。

因此,创建 viewModel 对象的最佳方法是使用委托 by(4, 5),两者相同,只是语法略有不同,我选择 4 是因为它的简单性和可读性。

val myViewModel4: MyViewModel by viewModels()

by 委托提供了延迟加载实例的灵活性,如果您尝试在没有委托的情况下在全局范围内初始化 viewModel,您可以在全局范围内定义 viewModel 并摆脱样板代码该应用程序将崩溃,因为 viewModel 将在创建 activity 之前尝试初始化(它不会延迟加载 viewModel 实例)。

现在让我们看看如何使用多个参数进行延迟加载,6th问题中未提及的方法。

如果你的视图模型中有多个参数并且没有使用任何依赖注入,你可以使用 ViewModelFactory 实现然后延迟加载它:

val myViewModelWithParm: MyViewModel by viewModels { MyViewModelFactory(application, "param1", "param2") }

ViewModelFactory 实现:

    class MyViewModelFactory(val application: Application, val param1: String, val param2: String) :
    ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return MyViewModel(application, param1, param2) as T
    }
}

至此我们已经清楚委托初始化(4, 5),以及它与(1, 2, 3) 的不同之处现在让我们看看前 3 种方法(1, 2, 3) 的区别).

我们先检查 1 和 2。

  1. val myViewModel1 = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(MyViewModel::class.java)
  2. myViewModel2 = ViewModelProvider.AndroidViewModelFactory(this.application).create(MyViewModel::class.java)

它们的主要区别是一个使用ViewModelProvider.NewInstanceFactory,另一个使用ViewModelProvider.AndroidViewModelFactory,所以我检查了两个类的源代码,发现ViewModelProvider.AndroidViewModelFactory是实际上 ViewModelProvider.NewInstanceFactory 的实现覆盖了 create 函数,这意味着两者都在做同样的事情,如果我们想要多个参数,最好选择这两种方法,但是为此我们必须覆盖 ViewModelProvider.NewInstanceFactory像完成一样创建我们自己的工厂

现在是第三个:

val myViewModel3 = ViewModelProvider(this).get(MyViewModel::class.java)

当我们的 ViewModel 中没有多个参数并且不想延迟加载对象时,这是 1 和 2 的简单形式。

注意:我强烈推荐方法4或5(两者都是相同的,但语法不同),因为这是最适合和最佳的写法,如果你不这样做的话有多个参数,如果您有多个参数,您可以通过实施 ViewModelProvider.Factory.

使用答案中提到的方法 6