zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

JetPack组件学习ViewModel

2023-09-27 14:25:57 时间
ViewModel的使用

1.需要先创建ViewModel类 继承自ViewModel重写onclear方法 使得页面销毁的时候能够走到自定义的onClear方法中

class MyViewModel : ViewModel() {

 //共享数据的核心在于拿到同一个LiveData实例 也就是拿到同一个ViewModel实例 其保存在ViewModelStore中

 //而ViewModelStore是Activity/Fragment提供的 做了屏幕转换的恢复处理 ViewModelStore会保存其数据 

 var progress:MutableLiveData Int ? null

 override fun onCleared() {

 //页面销毁回调

 super.onCleared()

 Log.i( wwwwwwwwwwwwwwwww , onCleared:MyVBiewModel cleared )

}

2 创建ViewModelProvider

在Activity中创建ViewModelProvider实例需要ViewModelOwner作为参数

和LifeCyclerOwner一样都是CommpentActivity实现的接口

除此之外还需要一个工厂。

该工厂默认实现是获取get函数传入的class反射创建ViewModel实例 也可以自定义工厂函数 会接受一个class的参数只需要返回该实例即可 中间的操作可以自定义

一 传入ViewModelOwner Activity/Fragment已经实现该接口

ViewModelProvider(this)

.get(MyViewModel::class.java) //默认实现反射创建ViewModel实例

二 创建实例过程自定义返回ViewModel实例即可

ViewModelProvider(this,object :ViewModelProvider.Factory{

 override fun T : ViewModel? create(modelClass: Class T ): T {

 modelClass.constructors

 return modelClass.getConstructor(Application::class.java).newInstance(application)

 }).get(RoomViewModel::class.java).

上面是利用反射创建了一个带有参数的ViewModel。默认创建的是无参的实例

3.通过get传入对应的Viewmodel的Class对象即可。

简要分析

首先创建ViewModelProvider实例 看下对应源码

public ViewModelProvider( NonNull ViewModelStoreOwner owner, NonNull Factory factory) {

 //第一个参数调用其getViewModelStore函数

 //第二个参数是个工厂稍后分析

 this(owner.getViewModelStore(), factory);

 }

可以看到Fragment和ComponentActivity都实现了该接口

image

接下来查看ComponentActivity。

1.首次mViewModelStore肯定为null 从nc中取出肯定也取不到只能通过new的方式去创建

2.当经历了屏幕旋转这时候就会从configure中取出viewmodelStore了 对应的也就是第二个红框 最后说明这个流程。

image

创建完ViewModelProvider后 调用get方法获取Viewmodel实例。

private static final String DEFAULT_KEY 

 androidx.lifecycle.ViewModelProvider.DefaultKey 

 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 

 //传入两个参数 

 //第一个是之后做缓存用的 DEFAULT_KEY 是常量

 //第二个是自定义ViewModel的class

 return get(DEFAULT_KEY : canonicalName, modelClass);

 }
 NonNull

 MainThread

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

 //首先从缓存中获取 viewmodelStore可以看成是一个map 保存ViewModel

 ViewModel viewModel mViewModelStore.get(key);

 //可以看到mFactory 分为两类 

 1.OnRequeryFactory 当缓存命中后该方法会回调并将命中的viewmodel传入

 2.KeyedFactory 继承自OnRequeryFactory 并提供create函数提供class创建实例过程有用户自定义

 if (modelClass.isInstance(viewModel)) {

 //OnRequeryFactory是缓存命中后的回调

 if (mFactory instanceof OnRequeryFactory) {

 ((OnRequeryFactory) mFactory).onRequery(viewModel);

 return (T) viewModel;

 } else {

 //noinspection StatementWithEmptyBody

 if (viewModel ! null) {

 // TODO: log a warning.

 //调用create函数将class传入 内部使用不同的构造方法创建实例并返回

 if (mFactory instanceof KeyedFactory) {

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

 } else {

 viewModel (mFactory).create(modelClass);

 //缓存该ViewModel

 mViewModelStore.put(key, viewModel);

 return (T) viewModel;

 }

ViewModelStore源码

public class ViewModelStore {

//可以看到就是个map缓存

 private final HashMap String, ViewModel mMap new HashMap ();

 final void put(String key, ViewModel viewModel) {

 ViewModel oldViewModel mMap.put(key, viewModel);

 if (oldViewModel ! null) {

 oldViewModel.onCleared();

 final ViewModel get(String key) {

 return mMap.get(key);

 Set String keys() {

 return new HashSet (mMap.keySet());

 * Clears internal storage and notifies ViewModels that they are no longer used.

 public final void clear() {

 for (ViewModel vm : mMap.values()) {

 vm.clear();

 mMap.clear();

}
问答如何实现旋转屏幕数据保持不变

答 1.第一次创建 首先会从对应的Activity中的NoLastConfigure获取activity取出对应的ViewModelStore这个时候由于是第一次所以是null 于是只能new一个ViewModelStore。

2.Activity重建时会销毁页面将ViewmodelStore保存到lastCongiure中并保存到ActivityClientRecord中传递给AMS端。

3.AMS重新调用 这里需要注意如果是配置引起的重建会走RelauchActivity而不是第一次普通的lauchActivity ReLaunch会通过token取出对应的AcRecord 在attach的时候将record中上一次保存的lastCoinfigure取出来

4.onCreate的时候把store赋值给NoLastConfigure 这个时候页面执行onCreate获取 ViewModelStore就可以获取到了 而且Store是保存着这个页面的所有Viewmodel所以上一次的ViewModel中的数据还在并没有销毁

详细流程

HandlerRelauncherActivity中先调用handlerdestory销毁页面保存重要配置到record中 AMS会保存token{Activity唯一标识}和record的map

在调用lauchActivity重建页面通过token重新取出record record在取出configure保存到新创建的activity的属性中。

1.当调用performDestory的时候创建一个Configure类取出viewmodelStore中如果没有直接取到从上一次的configure中取 创建完configure后保存

到record中的lastConfigure属性中。

2.何时重建 在performLaunchActivity方法中调用attach方法 在这个方法中取出record中的lastConfigure赋值给成员变量mLastConfigure

和之前的Presenter有什么区别

个人感觉

1.持有V层引用这个很好地解决了 但是回调V层还是得利用很多接口进行传递数据 这种主动通知V层的方式虽然变成接口回调的方式本质上耦合还是严重 可以通过LiveData V层去观察ViewModel中的数据变化这样耦合会降低一些

2.当配置失效比如屏幕旋转会销毁重建Activity 数据虽说可以通过onSavedInstance来传递 但是数据量并不能太大。但是ViewModel是系统原生支持的我们可以直接获取到上次销毁的ViewModel实例数据还在其中

3.具有生命周期可以自动管理防止泄漏 可通过onCleared告知持有该ViewModel的V层销毁

4.缓存命中和创建Viewodel都有回调 可以做自定义处理



Android Jetpack系列之ViewModel ViewModel的定义:**ViewModel旨在以注重生命周期的方式存储和管理界面相关的数据**。ViewModel本质上是视图(View)与数据(Model)之间的桥梁,想想以前的MVC模式,视图和数据都会写在Activity/Fragment中,导致Activity/Fragment过重,后续难以维护,而ViewModel将视图和数据进行了分离解耦,为视图层提供数据。
大放光彩的安卓Jetpack组件-ViewModel(终) 前面我们已经说过Jetpack中ViewModel的作用、用法以及使用要点,但还缺少在Activity中的实例展示,所以本节我们将结合结果展示与代码进行解读,希望能更好的展示出ViewModel的风采。
大放光彩的安卓Jetpack组件-ViewModel(二) 上回我们说到使用方法,但没有具体去说明使用的要点,其实ViewMode还是挺容易上手的,这节我们来具体说明一些使用要点与运用方式。