ViewModel初识

问题

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

图片来自官网

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

以前,您可能曾经 onRetainCustomNonConfigurationInstance 在配置更改期间保存此数据,然后在另一端将其解析获取。但是,如果您的数据不需要知道或管理“活动”所处于的生命周期状态,那会不会更好呢?除了scoreTeamA 将数据存储在 Activity 内,而不是将其存储在 Activity 内,而是将其存储在Activity之外的其他地方,该怎么办?这是ViewModel类的目的。

在下图中,您可以看到一个活动的生命周期,该活动经历一个轮换然后最终完成。ViewModel 的生命周期显示在关联的Activity生命周期旁边。请注意,ViewModels 可以轻松地与 Fragment 和 Activity 结合使用,我将它们称为UI控制器。本示例重点介绍活动。

图片来自官网

从您首次请求ViewModel时(通常在onCreateActivity中)到活动完成并销毁为止,ViewModel 存在。onCreate在Activity的生命周期内可能会多次调用(例如,旋转应用程序时),但ViewModel会在整个过程中保留下来。

一个非常简单的例子

设置和使用 ViewModel 分为三个步骤:

  1. 通过创建扩展 ViewModel 的类,将数据与UI控制器分离

  2. 设置 ViewModel 和UI控制器之间的通信

  3. 在 UI 控制器中使用 ViewModel

步骤1:创建一个ViewModel类

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

 // ViewModel and LiveData
 implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
 // alternatively - just ViewModel
 implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" 
 // For Kotlin use lifecycle-viewmodel-ktx

通常,您将为应用程序中的每个屏幕创建一个ViewModel类。这个ViewModel类将保存与屏幕关联的所有数据,并具有用于存储的数据的getter和setter。这会将代码显示出来,以显示 UI(在“Activities”和“Fragments”中实现)与数据(现在位于ViewModel中)之间。因此,让我们为Court-Counter中的一个屏幕创建一个ViewModel类:

class ScoreViewModel : ViewModel() {

    var scoreTeamA: Int = 0

    var scoreTeamB: Int = 0
}

步骤2:关联控制器和ViewModel

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

但是,ViewModels 不应保留对Activity,Fragment或Context的引用。此外,ViewModels不应包含包含对UI控制器(例如Views)的引用的元素,因为这将创建对Context 的间接引用。

您不应该存储这些对象的原因是 ViewModels 的寿命超过了特定的UI控制器实例-如果您将Activity旋转3次,则您已经创建了三个不同的Activity实例,但是只有一个ViewModel

考虑到这一点,让我们创建此UI控制器/ ViewModel关联。您将要在UI Controller中为ViewModel创建一个成员变量。对于Court-Counter,它是这样的:

import androidx.activity.viewModels
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private val viewModel: ScoreViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        tvOne.text = viewModel.scoreTeamA.toString()
        tvTwo.text = viewModel.scoreTeamB.toString()

        btAdd.setOnClickListener {
            viewModel.scoreTeamA += 1
            tvOne.text = viewModel.scoreTeamA.toString()
        }
        Log.d("MainActivity", "onCreate 执行了")
    }
}

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

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

要访问或更改UI数据,现在可以在ViewModel中使用数据。这是新onCreate方法的示例,以及通过向A组增加一个点来更新分数的方法:

import androidx.activity.viewModels
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private val viewModel: ScoreViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        tvOne.text = viewModel.scoreTeamA.toString()
        tvTwo.text = viewModel.scoreTeamB.toString()

        btAdd.setOnClickListener {
            viewModel.scoreTeamA += 1
            tvOne.text = viewModel.scoreTeamA.toString()
        }
        Log.d("MainActivity", "onCreate 执行了")
    }
}

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

你可能感兴趣的:(ViewModel初识)