【译】ViewModels : 一个简单的例子

栏目: IOS · Android · 发布时间: 4年前

内容简介:两年多前,我正在开发Android初学者;一个课程,将学生从零编程带到他们的第一个Android应用程序。作为课程的一部分,学生们构建了一个非常简单的一个名为Court-Counter 是一个非常简单的应用程序,带有修改篮球得分的按钮。完成的应用程序虽然有一个bug; 如果你旋转手机,你的当前分数将莫名其妙地消失。这是怎么回事?旋转设备是应用程序在其生命周期内可以进行的一些配置更改之一,包括键盘可用性和更改设备的语言。所有这些配置更改都会导致活动被拆除并重新创建。

两年多前,我正在开发Android初学者;一个课程,将学生从零编程带到他们的第一个Android应用程序。作为课程的一部分,学生们构建了一个非常简单的一个名为 Court-Counter 的屏幕应用程序。

Court-Counter 是一个非常简单的应用程序,带有修改篮球得分的按钮。完成的应用程序虽然有一个bug; 如果你旋转手机,你的当前分数将莫名其妙地消失。

【译】ViewModels : 一个简单的例子

这是怎么回事?旋转设备是应用程序在其生命周期内可以进行的一些配置更改之一,包括键盘可用性和更改设备的语言。所有这些配置更改都会导致活动被拆除并重新创建。

这种行为允许我们在设备旋转时使用横向方向特定布局。不幸的是,对于新的(有时候不是那么新的)工程师来说,他们可能会头疼。

在Google I / O 2017中,Android Framework 团队引入了一组新的架构组件,其中一个处理这个确切的轮换问题。

该视图模型类是旨在支持并在生命周期意识的方式管理UI相关的数据。这允许数据在配置更改(如屏幕旋转)后继续存在。

这篇文章是探索 ViewModel 细节的系列文章中的第一篇。在这篇文章中,我将:

  • 解释 ViewModels 实现的基本需求
  • 通过更改 Court-Counter 代码以使用 ViewModel 来解决轮换问题
  • 仔细查看 ViewModel 和 UI Component 关联

根本问题

潜在的挑战是 Android Activity 生命周期 有很多状态,并且由于配置更改,单个 Activity 可能会多次循环通过这些不同的状态。

【译】ViewModels : 一个简单的例子

当一个Activity正在经历所有这些状态时,您可能还需要保留在内存中的瞬态UI数据。我将把瞬态UI数据定义为UI所需的数据。示例包括用户输入的数据,运行时生成的数据或从数据库加载的数据。这些数据可以是位图图像,RecyclerView所需的对象列表,或者在这种情况下是篮球得分。

以前,您可能习惯 onRetainNonConfigurationInstance在 配置更改期间保存此数据并在另一端解压缩。但是,如果您的数据不需要知道或管理 Activity 所处的生命周期状态,那么它不会膨胀吗?而不是像 scoreTeamA Activity 中那样拥有变量,因此与 Activity 生命周期的所有奇思妙想相关联,如果该数据存储在 Activity 之外的其他地方,该怎么办?这是 ViewModel 类的目的。

在下图中,您可以看到活动的生命周期,该活动经历轮换然后最终完成。ViewModel 的生命周期显示在关联的 Activity 生命周期旁边。请注意,ViewModel 可以很容易地与 Fragments 和 Activities 一起使用,我称之为 UI 控制器。此示例重点关注活动。

【译】ViewModels : 一个简单的例子

ViewModel 从您第一次请求 ViewModel(通常在 onCreate Activity 中)到 Activity 完成并销毁之时就存在。 onCreate 可以在活动的生命周期中多次调用,例如当应用程序旋转时,但 ViewModel 始终存在。

一个非常简单的例子

设置和使用 ViewModel 有三个步骤:

  1. 通过创建扩展 ViewModel 的类,从 UI 控制器中分离出数据
  2. 设置 ViewModel 和 UI 控制器之间的通信
  3. 在UI控制器中使用 ViewModel

第1步:创建一个 ViewModel 类

注意:要创建 ViewModel,首先需要添加正确的生命周期依赖项。看看这里怎么样。

通常,您将为应用中的每个屏幕创建一个 ViewModel 类。此 ViewModel 类将保存与屏幕关联的所有数据,并具有存储数据的 getter 和 setter。这将代码分离,以显示您的活动和片段中实现的 UI,该数据现在位于 ViewModel 中。那么,让我们在 Court-Counter 中为一个屏幕创建一个 ViewModel 类:

public class ScoreViewModel extends ViewModel {
   // Tracks the score for Team A
   public int scoreTeamA = 0;

   // Tracks the score for Team B
   public int scoreTeamB = 0;
}
复制代码

ScoreViewModel.java 为简洁起见,我选择将数据存储为公共成员,但创建 getter 和 setter 以更好地封装数据是一个好主意。

第2步:关联 UI 控制器和 ViewModel

您的 UI 控制器(也称为 Activity 或 Fragment )需要了解您的 ViewModel。这样您的UI控制器就可以在 UI 交互发生时显示数据并更新数据,例如按下按钮以增加团队在 Court-Counter 中的得分。

但是,ViewModels 不应该包含对 Activities,Fragments 或 Context 的引用。**此外,ViewModel 不应包含包含对UI控制器的引用的元素,例如 Views,因为这将创建对 Context 的间接引用。

您不应存储这些对象的原因是 ViewModels 比您的特定 UI 控制器实例更长 - 如果您将 Activity 旋转三次,您刚刚创建了三个不同的 Activity 实例,但您只有一个 ViewModel。

考虑到这一点,让我们创建这个 UI 控制器/ViewModel 关联。您需要在 UI 控制器中为 ViewModel 创建成员变量。然后 onCreate ,你应该调用:

ViewModelProviders.of(<Your UI controller>).get(<Your ViewModel>.class)
复制代码

在 Court-Counter 的情况下,这看起来像:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   mViewModel = ViewModelProviders.of(this).get(ScoreViewModel.class);
   // Other setup code below...
}
复制代码

**注意: “ViewModels 中的无上下文”规则有一个例外。有时您可能需要一个Application 上下文 (而不是 Activity 上下文)来与系统服务一起使用。在 ViewModel 中存储应用程序上下文是可以的,因为应用程序上下文与应用程序生命周期相关联。这与 Activity 上下文不同,后者与 Activity 生命周期相关联。实际上,如果您需要 Application 上下文,则应该扩展AndroidViewModel,它只是一个包含 Application 引用的 ViewModel。

第3步:在 UI 控制器中使用 ViewModel

要访问或更改 UI 数据,您现在可以使用 ViewModel 中的数据。以下是 onCreate 通过向团队A添加一个点来更新分数的新方法和方法的示例:

专业提示: ViewModel 也可以与另一个体系结构组件LiveData 很好地配合,我将不会在本系列中深入探讨。使用 LiveData 的额外好处是它可以观察到:它可以在数据发生变化时触发 UI 更新。您可以在此处了解有关 LiveData 的更多信息。

// 完成的 onCreate 方法
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   mViewModel = ViewModelProviders.of(this).get(ScoreViewModel.class);
   displayForTeamA(mViewModel.scoreTeamA);
   displayForTeamB(mViewModel.scoreTeamB);
}

// ViewModel 读取和写入的示例
public void addOneForTeamA(View v) {
   mViewModel.scoreTeamA = mViewModel.scoreTeamA + 1;
   displayForTeamA(mViewModel.scoreTeamA);
}
复制代码

仔细看看 ViewModelsProviders.of

ViewModelProviders.ofMainActivity 第一次调用该方法时,它会创建一个新的 ViewModel 实例。当再次调用此方法时,无论何时 onCreate 调用此方法,它都将返回与特定 Court-Counter MainActivity 关联的预先存在的 ViewModel。这是保留数据的原因。

仅当您将正确的 UI 控制器作为第一个参数传递时,此方法才有效。虽然您永远不应该在 ViewModel 中存储 UI 控制器,但 ViewModel 类会使用您传入的 UI 控制器作为第一个参数来跟踪 ViewModel 和幕后 UI 控制器实例之间的关联。

ViewModelProviders.of(<THIS ARGUMENT>).get(ScoreViewModel.class);
复制代码

这允许您拥有一个应用程序,可以打开相同活动或片段的许多不同实例,但具有不同的 ViewModel 信息。让我们想象一下,如果我们扩展我们的 Court-Counter 示例来获得多个篮球比赛的分数。游戏以列表形式呈现,然后单击列表中的游戏将打开一个看起来像我们当前 MainActivity 的屏幕,但我将其称为 GameScoreActivity。

对于您打开的每个不同的游戏屏幕,如果您将 ViewModel 与 GameScoreActivityin 关联 onCreate ,它将创建一个不同的 ViewModel 实例。如果旋转其中一个屏幕,则会保持与同一 ViewModel 的连接。

【译】ViewModels : 一个简单的例子

所有这些逻辑都是通过调用完成的 ViewModelProviders.of(<Your UI controller>).get(<Your ViewModel>.class) 。因此,只要您传入正确的UI控制器实例,它就可以正常工作。

最后一点想法:ViewModel 非常适合将 UI 控制器代码与填充 UI 的数据分开。也就是说,它们并不能解决数据持久性和保存应用状态的问题。在下一篇文章中,我将探讨 Activity 生命周期与 ViewModels 的微妙交互以及 ViewModels 的比较方式 onSaveInstanceState。

结论和进一步学习

在这篇文章中,我探讨了新 ViewModel 类的基础知识。关键要点是:

  • 该视图模型类是旨在支持并在生命周期意识的方式管理 UI 相关的数据。这允许数据在配置更改(如屏幕旋转)后继续存在。
  • ViewModels 将 UI 实现与应用程序的数据分开。
  • 通常,如果应用中的屏幕具有瞬态数据,则应为该屏幕的数据创建单独的 ViewModel。
  • ViewModel 的生命周期从首次创建关联的 UI 控制器时开始,直到完全销毁。
  • 切勿直接或间接地将 UI 控制器或 Context 存储在 ViewModel 中。这包括在 ViewModel 中存储 View。对 UI 控制器的直接或间接引用会破坏将 UI 与数据分离的目的,并可能导致内存泄漏。
  • ViewModel 对象通常会存储 LiveData 对象,您可以在此处了解更多信息。
  • ViewModelProviders.of 方法跟踪什么 UI 控制器视图模型与通过传递作为参数的 UI 控制器相关联。

想要更多ViewModel-ly goodness?查看:

  • 添加gradle依赖项的说明
  • ViewModel 文档
  • 使用带有视图 和生命周期 Codelab 的Room 引导 ViewModel 练习

架构组件是根据您的反馈创建的。如果您对 ViewModel 或任何架构组件有任何疑问或意见,请查看我们的反馈页面。关于这个系列的问题或建议?发表评论!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

精通正则表达式

精通正则表达式

弗里德尔 / 东南大学出版社 / 2005-9 / 72.00元

正则表达式是一种用来操作文本和数据的强大工具。近年来,它们快速广泛传播,并被多种流行工具和语言作为标准特性提供,如Perl、Java、VB.NET、C#(及任何使用.NET框架的语言)、PHP、Python、Ruby、Tcl、MySQL、awk、Emacs等。 如果还未使用过正则表达式,从本书中您将发现一个掌控数据的全新世界。如果使用过它们,您将会充分意识到本书空前的深度和广度。如果您认为自己已经......一起来看看 《精通正则表达式》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具