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

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

内容简介:两年多前,我正在开发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 或任何架构组件有任何疑问或意见,请查看我们的反馈页面。关于这个系列的问题或建议?发表评论!


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

查看所有标签

猜你喜欢:

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

Learn Python the Hard Way

Learn Python the Hard Way

Zed A. Shaw / Addison-Wesley Professional / 2013-10-11 / USD 39.99

Master Python and become a programmer-even if you never thought you could! This breakthrough book and CD can help practically anyone get started in programming. It's called "The Hard Way," but it's re......一起来看看 《Learn Python the Hard Way》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具