讲讲Android 架构组件 ViewModel 的那些事

引言

关于 ViewModel ,Android 开发的小伙伴应该都非常熟悉,无论是新项目还是老项目,基本都会使用到。而 ViewModel 作为 JetPack 核心组件,其本身也更是承担着不可或缺的作用。

因此,了解 ViewModel 的设计思想更是每个应用层开发者必不可缺的基本功。

随着这两年 ViewModel 的逐步迭代,比如 SaveStateHandle 的加入等,ViewModel 也已经不是最初版本的样子。要完全理解其设计体系,往往也要伴随着其他组件的基础,所以并不是特别容易能被开发者吃透。

故本篇将以最新视角开始,与你一起,用力一瞥 ViewModel 的设计原理。

本文对应的组件版本:

  • Activity-ktx-1.5.1
  • ViewModel-ktx-2.5.1

本篇定位中等,将从背景与使用方式开始,再到源码解读。由浅入深,解析 ViewModel 的方方面面。

导航

学完本篇,你将了解或明白以下内容:

  • ViewModel 的使用方式;
  • SavedStateHandle 的使用方式;
  • ViewModel 创建与销毁流程;
  • SavedStateHandle 创建流程;

好了,让我们开始吧! 🐊

基础概念

在开始本篇前,我们先解释一些基础概念,以便更加清晰的了解后续的状态保存相关。

何谓配置变更?

配置变更指的是,应用在运行时,内置的配置参数变更从而触发的Activity重新创建

常见的场景有:旋转屏幕、深色模式切换、屏幕大小变化、更改了默认语言或者时区、更改字体大小或主题颜色等。

何谓异常重建?

异常重建指的是非配置变更情况下导致的 Activity 重新创建。

常见场景大多是因为 内存不足,从而导致后台应用被系统回收 ,当我们切换到前台时,从而触发的重建,这个机制在Android中为 Low Memory Killer 机制,简称 LMK

可以在开发者模式,限制后台任务数为1,从而测试该效果。

ViewModel存在之前的世界

在 ViewModel 出现之前,对于 View 逻辑与数据,我们往往都是直接存在 Activity 或者 Fragment 中,优雅一点,会细分到具体的单独类中去承载。当配置变更时,无可避免,会触发界面重绘。相应的,我们的数据在没有额外处理的情况下,往往也会被初始化,然后在界面重启时重新加载。

但如果当前页面需要维护某些状态不被丢失呢,比如 选择、上传状态 等等? 此时问题就变得棘手起来。

稍有经验同学会告诉你,在 onSaveInstanceState 中重写,使用bundle去存储相应的状态啊?➡️

但状态如果少点还可以,多一点就非常头痛,更别提包含继承关系的状态保存。 😶‍🌫️

所以,不出意外的话,我们 App 的 Activity-manifest 中通常默认都是下列写法:

android:configChanges="keyboard|orientation|uiMode|..."

这也是为啥Android程序普遍不支持屏幕旋转的一部分原因,从源头扼杀因部分配置变更导致的状态丢失问题。🐶保命

VideModel存在之后的世界

随着 ViewModel 组件推出之后,上述因配置变更而导致的状态丢失问题就迎刃而解。

ViewModel 可以做到在配置变更后依然持有状态。所以,在现在的开发中,我们开始将 View数据 与 逻辑 藏于 ViewModel 中,然后对外部暴漏观察者,比如我们常常会搭配 LiveData 一起使用,以此更容易的保持状态同步。

关于 ViewModel 的生命周期,具体如下图所示:

虽然 ViewModel 非常好用,但 ViewModel 也不是万能,其只能避免配置变更时避免状态丢失。比如如果我们的App是因为 内存不足 而被系统kill 掉,此时 ViewModel 也会被清除 🔺 。

不过对于这种情况,仍然有以下三个方法可以依然保存我们的状态:

  • 重写 onSaveInstanceState() 与 onRestoreInstanceState();
  • 使用 SavedState,本质上其实还是 onSaveInstanceState() ;
  • 使用 SavedStateHandle ,本质上是依托于 SaveState 的实现;

上述的后两种都是随着 JetPack 逐步被推出,可以理解为是对原有的onSavexx的封装简化,从而使其变得更易用。

关于这三种方法,我们会在 SavedStateHandle 流程解析中再进行具体叙述,这里先提出来,留个伏笔。

ViewModel使用方式

作为文章的开始,我们还是要先聊一聊 ViewModel 的使用方式,如下例所示:

当然,你也可以选择引入 activity-ktx ,从而以更简便的写法去写:

implementation 'androidx.activity:activity-ktx:1.5.1'

private val mainModel by viewModels<MainViewModel>()

示例比较简单,我们创建了一个 ViewModel ,如上所示,并在 MainActivity 的 onCreate() 中进行了初始化。

这也是我们日常的使用方式,具体我们

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值