Android mvvm框架之ViewModel(1)

本文详细介绍了Android的ViewModel如何解决数据与Controller过度耦合问题,阐述了ViewModel的实现原理,包括ViewModel类的抽象设计、构造过程以及与宿主Controller生命周期保持一致的原因。此外,文章还涉及了ViewModelStore的使用和在配置变化时的保持策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.4 ViewModel 解决的问题

ViewModel 是 android 新的 mvvm 框架的一部分,它的出现就是为了解决以上两个场景中数据与 Controller 耦合过度的问题。其 基本原理 是:维护一个与配置无关的对象,该对象可存储 Controller 中需要的任何数据,其生命周期与宿主 Controller 的生命周期保持一致,不因 Controller 的重建而失效(注意:Controller 的重建仍然在 Controller 生命周期内,并不会产生一个新的生命周期,即 Controller 的 onDestroy 并不会调用)

这意味着无论是转屏还是系统字体变化等因配置变化产生的 Controller 重建都不会回收 ViewModel 中维护的数据,重建的 Controller 仍然可以从同一个 ViewModel 中通过获取数据恢复状态。

2 ViewModel 实现原理

2.1 ViewModel 类

如果大家去看一下 ViewModel 类的实现,会发现虽然它是一个 abstract 类,但是没有暴露任何外部可访问的方法,其预留的方法都是 package 访问权限的, 其预留了一些数据清理工作的功能,推测可能是系统保留用作以后扩展,因为与我们对 ViewModel 原理的理解没有什么关联,我们暂且略过。

2.2 ViewModel 的构造过程

我们用一个结构图来剖析 ViewModel 的构造过程:

How to get a ViewModel

如图所示:

  • 所有已经实例化的 ViewModel 都缓存在一个叫做 ViewModelStore 的封装对象中,其实质是一个 HashMap;

  • ViewModelStore 与具体的 Controller 绑定,并与宿主 Controller 俱生俱灭,所以这就解释了为何 ViewModel 与宿主 Controller 的生命周期是一样长了,因为缓存它的 ViewModelStore 与宿主 Controller 寿命相等;

  • 获取 ViewModel 实例的过程委托给了一个叫做 ViewModelProvider 的工具类,它包含一个创建 ViewModel 的工厂类 Factory 和一个对 ViewModelStore 的引用;

  • 总的构造过程为:先从 ViewModelStore 中获取缓存的 ViewModel,若没有缓存过则用 Facotry 实例化一个新的 ViewModel 并缓存,具体的过程分为 4 步,具体可参考图示。

本小节剩下部分分析源码,对于只关心原理的同学此部分可以略过:

我们在获取 ViewModel 的时候,一般通过如下方式:

// 在 Controller(这里以 Fragment 为例)的 onCreate 方法中调用

final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);

我们看一下 ViewModelProviders.of() 的实现:

public static ViewModelProvider of(@NonNull Fragment fragment) {

return of(fragment, null);

}

public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {

Application application = checkApplication(checkActivity(fragment));

if (factory == null) {

factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);

}

// 最终用宿主 Controller 的 ViewModelStore 和一个 Factory 实例化一个

// ViewModelProvider

return new ViewModelProvider(fragment.getViewModelStore(), factory);

}

我们再看一下 ViewModelProvider.get() 方法获取 ViewModel 实例的过程:

public T get(@NonNull Class modelClass) {

String canonicalName = modelClass.getCanonicalName();

if (canonicalName == null) {

throw new IllegalArgumentException(“Local and anonymous classes can not be ViewModels”);

}

// 我们看到了 ViewModel 在 ViewModelStore 中的 key 表示

return get(DEFAULT_KEY + “:” + canonicalName, modelClass);

}

public T get(@NonNull String key, @NonNull Class modelClass) {

// 先检查缓存中是否存在

ViewModel viewModel = mViewModelStore.get(key);

if (modelClass.isInstance(viewModel)) {

//noinspection unchecked

return (T) viewModel;

} else {

//noinspection StatementWithEmptyBody

if (viewModel != null) {

// TODO: log a warning.

}

}

// 缓存中没有,通过 Factory 构造

if (mFactory instanceof KeyedFactory) {

viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);

} else {

viewModel = (mFactory).create(modelClass);

}

// 新实例保存缓存

mViewModelStore.put(key, viewModel);

//noinspection unchecked

return (T) viewModel;

}

3 ViewModel 与配置无关的原理(与宿主 Controller 俱生俱灭)

上一节我们说到,ViewModel 之所以能够与宿主 Controller 保持生命周期一致,是因为存储它的 ViewModelStore 与宿主 Controller 生命周期一致。那么为什么 ViewModelStore 能够保持和 Controller 生命周期一致呢?

这里我们需要先理清 FragmentActivity 和其寄生的 Fragment 的 ViewModelStore 之间的关系:

3.1 ViewModelStore 树

ViewModelStore Tree

如图所示:

  • 每个 ViewModelStore 依附于其宿主 Controller,所以各个 Controller 的 ViewModelStore 组成一个树状的引用关系;

  • 处于顶层的 ViewModelStore 依附于 FragmentActivity,它除了保存用户级的 ViewModel 以外,还保存其儿子 Fragment 的 FragmentManagerViewModel;

  • FragmentManagerViewModel 主要维护两个对象:所属 Fragment 的 ViewModelStore 和其儿子 Fragment 的 FragmentManagerViewModel 的引用,注意图中的红色部分,所有二级及以下的子孙 Fragment 都共用同一个父节点的 Child FragmentManagerModel,这样当父 Fragment 销毁的时候方便一次性清除其所有子 Fragment 共用的 FragmentManagerViewModel;

  • 但是二级及以下的子孙 Fragment 的 ViewModelStore 都是独立的,一个 Fragment 自身的 ViewModel 变化应该不影响其兄弟节点的 ViewModel,所以可以推导出,它们共同的 FragmentManagerViewModel 应该是维护了一个保存各个子 Fragment 的 ViewModelStore 的容器,大家如果细看 FragmentManagerViewModel 的源代码,实际上就是这么做的。

所以,我们看到,处于顶层的 FragmentActivity 的 ViewModelStore 是一个超级 Store,它引用了所有的 ViewModels,包括自身的数据、所有子孙 Fragment 的 ViewModels,只要各子孙 Fragment 不清除自有 ViewModelStore,则所有的数据都维护在这棵 ViewModelStore 树中。

那么在配置发生变化的时候,ViewModelStore 树如何保持不变呢?

3.2 系统级的配置无关支持

将 ViewModelStore 作为配置无关数据进行保持,在 FragmentActivity 中是这么做的:

Retain ViewModelStore in activity

是的,流程就是这么简单,只需要将 ViewModelStore 封装在一个特殊对象中保存并在 FragmentActivity 的 onRetainNonConfigurationInstance() 方法中返回即可:

/**

  • Called by the system, as part of destroying an

  • activity due to a configuration change, when it is known that a new

  • instance will immediately be created for the new configuration. You

  • can return any object you like here, including the activity instance

  • itself, which can later be retrieved by calling

  • {@link #getLastNonConfigurationInstance()} in the new activity

  • instance.

*/

@Override

@Nullable

public final Object onRetainNonConfigurationInstance() {

Object custom = onRetainCustomNonConfigurationInstance();

ViewModelStore viewModelStore = mViewModelStore;

// …省略与原理无关代码

NonConfigurationInstances nci = new NonConfigurationInstances();

nci.custom = custom;

nci.viewModelStore = viewModelStore;

return nci;

}

这样,在顶层源头上就保证了所有 Controller 的 ViewModels 不会在发送配置变化的时候由于 Controller 重建而被销毁。

另外在 Fragment 层中,必须区分 Fragment 实例销毁时到底是因为调用了 onDestroy 还是配置发生了变化,如果是前者则必须清理自身持有的 ViewModelStore,如果是后者则不能清理:

How to clear Fragment's ViewModelStore

如图所示,也说明了 Fragment 的 ViewModel 生命周期与该 Fragment 生命周期是一致的。

// FragmentManagerImpl.moveToState()

//…省略

boolean beingRemoved = f.mRemoving && !f.isInBackStack(); // 是否 destroy,如果只是配置变化,则为 false

if (beingRemoved || mNonConfig.shouldDestroy(f)) {

boolean shouldClear;

if (mHost instanceof ViewModelStoreOwner) {

// 说明这是第一层 Fragment,只要顶层 ViewModelStore 没有清除该 FragmentManagerViewModel 就说明不用清理

shouldClear = mNonConfig.isCleared();

} else if (mHost.getContext() instanceof Activity) {

// 说明是 FragmentActivity 的子孙 Fragment,根据是否是配置变化来判断是否需要清理

Activity activity = (Activity) mHost.getContext();

shouldClear = !activity.isChangingConfigurations();

} else {

shouldClear = true;

}

if (beingRemoved || shouldClear) {

// 只有确实 destroy 了才需要清理

mNonConfig.clearNonConfigState(f);

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

作者2013年从java开发,转做Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

参与过不少面试,也当面试官 面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我整理了一份阿里P7级别的最系统的Android开发主流技术,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括阿里,以及字节跳动,腾讯,华为,小米,等一线互联网公司主流架构技术。如果你想深入系统学习Android开发,成为一名合格的高级工程师,可以收藏一下这些Android进阶技术选型

我搜集整理过这几年阿里,以及腾讯,字节跳动,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

Java语言与原理;
大厂,小厂。Android面试先看你熟不熟悉Java语言

高级UI与自定义view;
自定义view,Android开发的基本功。

性能调优;
数据结构算法,设计模式。都是这里面的关键基础和重点需要熟练的。

NDK开发;
未来的方向,高薪必会。

前沿技术;
组件化,热升级,热修复,框架设计

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

我在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多,CodeChina上可见;

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

[外链图片转存中…(img-0BcJLXmC-1713720296501)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

我在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多,CodeChina上可见;

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值