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的更多信息。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容