Jetpack组件之数据绑定组件——ViewModel

Jetpack组件系列文章目录

  1. Jetpack组件之数据绑定组件——DataBinding
  2. Jetpack组件之生命周期感知组件——LifeCycles


前言

本文是Jetpack组件专题的第三篇,Jetpack是Google官方在2018年推出的一套组件库,用来帮助开发者更轻松地编写优质应用。包含四大模块:Architecture( 架构组件)、Foundation(基础组件)、Behavior(行为组件)、UI(界面组件)。

在这里插入图片描述

有之以为利,无之以为用。一起来学习先进的组件知识。

一、ViewModel是什么?

先看下官方介绍:

ViewModel is a class that is responsible for preparing and managing the data for an Activity or a Fragment. It also handles the communication of the Activity / Fragment with the rest of the application (e.g. calling the business logic classes).

ViewModel是一个管理和提供Activity或fragment页面数据的类。可以保存UI 界面的数据,可以解决activity或fragment之间的通信问题。

1、优点

  1. 用来存储界面的数据,解耦
  2. 屏幕旋转的时候依然可以保存数据
  3. 可以用来解决activity和fragment之间的通信

2、局限性

  1. ViewModel不能够持有View、Lifecycle、Acitivity引用,而且不能够包含任何包含前面内容的类,否则会导致内存泄露

二、使用步骤

1.添加依赖

        implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'

2.创建数据类

书接上文DataBinding,新建一个数据类,使用了数据刷新
在这里插入图片描述

然后进入正文,创建一个ViewModel用来保存上边的数据,数据通常使用LiveData来维护,后续再结合LiveData细谈。

在这里插入图片描述

3.ViewModel使用

使用方法很简单,在调用合适的地方初始化即可

		// 实例化ViewModel
        mUserViewModel = new ViewModelProvider(this).get(UserViewModel.class);

带参数的ViewModel怎么整?
继续

4.带参数的ViewModel的使用

数据类增加参数

在这里插入图片描述
ViewModel提供了一个默认的ViewModelProvider,使用其factory方法可以用来创建ViewModel

在这里插入图片描述
调用带参数的ViewModel

		// 实例化带参数的ViewModel
        mUserViewParamsModel = new UserViewModelFactory(mStu).create(UserViewParamsModel.class);
        mActivityBindingBinding = DataBindingUtil.setContentView(this, R.layout.activity_view_model);
        // 关联数据和绑定意图
        mActivityBindingBinding.setUser(mStu);
        mActivityBindingBinding.tvSet.setOnClickListener(v -> {
            // 改变数据,刷新数据
//            mStu.setName("项羽");

            mUserViewParamsModel.reName();
        });

5.fragment之间的通信

viewModel还可以在同属一个Activity之间的不同fragment进行通信,共用的一份ViewModel

  public class MyFragment extends Fragment {
       public void onStart() {
           UserModel userModel = new ViewModelProvider(requireActivity()).get(UserModel.class);
       }
   }

三、生命周期

上官图
在这里插入图片描述
从官方提供的ViewModel作用域不难看出,ViewModel生命周期作用在activity的从生到死,最后拥有者销毁了在调用一下清理onCleared(),所以因为ViewModel的长生命周期,所以不能持有View、Lifecycle、Acitivity引用,防止出现内存泄露。

非得要个context,咋整?

官方提供了一个AndroidViewModel,增加了一个获取Application的方法,直接继承即可。

另外这张图是页面旋转的Activity的生命周期,也解释了viewModel可以在页面旋转的时候依然可以保存数据的由来。

四、敲一下原理

上狠活

从实例化开始

mUserViewModel = new ViewModelProvider(this).get(UserViewModel.class);

进入get方法,ViewModelProvider首先是一个提供ViewModel的实用类,通过传入的类类型获取规范化的类名,通过组合key,加类类型查找ViewModel

 public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }

可以看到是通过mViewModelStore根据组合的key获取的ViewModel。
mViewModelStore 那么是什么呢?
继续看

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
        } else {
            viewModel = mFactory.create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

从介绍其实就看得出来,ViewModelStore就是一个保存管理ViewModel的类,通过HashMap的形式,以刚才组合的key对ViewModel进行存取。
在这里插入图片描述
返回上一步get方法,就是先去ViewModelStore查缓存,有就直接取出来。没找到则通过工厂模式实例化创建,思路很清晰了。

看下里面具体用到的东西

在这里插入图片描述
factory 就是一个创建ViewModel的接口,看下其实例化的地方
在这里插入图片描述
factory实例化是第三个方法,由第一个方法传递而来,第一个方法也是ViewModelProvider创建调用的方法。看下ViewModelStoreOwner

在这里插入图片描述

ViewModelStoreOwner 很简单就是用来获取ViewModelStore的方法,ViewModelStore刚才讲过是用来存取ViewModel的。继续看下getViewModelStore方法

在这里插入图片描述
查找这个getViewModelStore发现在ComponentActivity和Fragment中都有,很简单就是实例化一个ViewModelStore,示例是ComponentActivity中的方法,Fragment中类似,没有就新建一个。

有个重点,getLastNonConfigurationInstance是做什么的?ViewModelStore实例化先从
NonConfigurationInstance从获取 ViewModelStore实例,如果NonConfigurationInstance不存在,就new一个mViewModelStore。
并且还注意到,在onRetainNonConfigurationInstance()方法中 会把mViewModelStore赋值给NonConfigurationInstances:

在这里插入图片描述
在这里插入图片描述

onRetainNonConfigurationInstance()方法很重要:在Activity因配置改变 而正要销毁时,且新Activity会立即创建,那么系统就会调用此方法。 也就说,配置改变时 系统把viewModelStore存在了NonConfigurationInstances中。所以屏幕旋转的时候数据保存是这样来的?

方法是在Acticity.java中,它返回的是Acticity.java中的NonConfigurationInstances的属性activity,也就是onRetainNonConfigurationInstance()方法返回的实例。

那么mLastNonConfigurationInstances是怎么来的?

搜索发现是在 Acticity.java的attach()方法中,attach方法是为Activity关联上下文环境,是在Activity 启动的核心流程——ActivityThread的performLaunchActivity方法中调用,这里的lastNonConfigurationInstances是存在 ActivityClientRecord中的一个组件信息

/**
	Maps from activity token to local record of the activities that are preparing to be launched.
*/
final Map<IBinder, ActivityClientRecord> mLaunchingActivities =
        Collections.synchronizedMap(new ArrayMap<IBinder, ActivityClientRecord>());

所以,ActivityThread 中的 ActivityClientRecord 是不受 activity 重建的影响,那么ActivityClientRecord中lastNonConfigurationInstances也不受影响,那么其中的Object activity也不受影响,那么ComponentActivity中的NonConfigurationInstances的viewModelStore不受影响,那么viewModel也就不受影响了。

回头来看,ViewModelProvider调用了getViewModelStore第一步初始化了一个ViewModelStore;

接着看Provider的后半部分,判断owner属于哪个工厂创建的,NewInstanceFactory.getInstance())很简单,实现Factory方法,实例化和一个初始化ViewModel的方法,主要看HasDefaultViewModelProviderFactory这个类

在这里插入图片描述
HasDefaultViewModelProviderFactory是一个接口,用来获取factory方法,查找发现ComponentActivity和Fragment都实现了此接口
在这里插入图片描述
getDefaultViewModelProviderFactory实例化了一个SavedStateViewModelFactory

在这里插入图片描述
此构造方法获取了activity的Registry、lifecycle、intent中的数据、application,实例化一个factory,保存了activity相关的数据

综上

  1. ViewModelProvider(this)先是初始化一个ViewModelStore存储器,再初始化了一个ViewModelFactory,里面使用了反射
  2. ViewModelStore先从NonConfigurationInstances取出,没有则新建,屏幕旋转的时候ViewModelStore会存储进NonConfigurationInstances,保存数据不受activity重建的影响。onRetainNonConfigurationInstance()保存状态,getLastNonConfigurationInstance() 恢复。
  3. get(@NonNull Class modelClass)主要是初始化ViewModel,有缓存取缓存,无缓存则新建

五、总结

ViewModel就是一个UI界面的数据管理类,长生命周期,屏幕旋转的时候依然可以保存数据,支持从属一个activity的fragment之间的通信,是实现MVVM的重要组件之一。
一般都是结合LiveData组合使用,和LiveData之间的用法,后续继续介绍。

六、参考链接

  • https://www.jianshu.com/p/5fbde7a2189e
  • https://blog.51cto.com/u_15482826/4911080
  • https://blog.youkuaiyun.com/FrenkX72/article/details/126117111
  • https://developer.android.google.cn/topic/libraries/architecture/viewmodel?hl=zh_cn#samples
  • https://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&mid=2650245802&idx=1&sn=b908cb1227d0b233e0addf712b0e808c&chksm=88637bc5bf14f2d383fa2f6d74c2c1205d7b8652bd1f26a5b0ed34ba5c6a173afbfb0cdf572d&scene=27
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值