jetpack-ViewModel

概述

官方对ViewModel的解释:

ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。

作用

它在Android中事实上是为了解决一下两个问题:

  • UI组件间实现数据共享
  • Activity因配置更改重建时保留数据

对于第一条,如果不使用VM,那么各个UI组件都需要持有共享数据的引用,这样会带来两个麻烦:

  • 如果新增共享数据,则各个UI组件需要再次声明并初始化新增的共享数据
  • 某个组件对于数据的修改,没办法直接通知其他UI组件,需手动实现观察者模式

对于第二条,如果不使用VM,那么还是可以通过onSaveInstanceState保存的,但是如果数据量比较大,数据的序列化和反序列化都会产生一定的性能开销。

ViewModel的特点

和上面作用说的类似。

  • ViewModel支持恢复大量数据,还不需要序列化和反序列化。而onSaveInstanceState只适用于少量数据,而且它还需要对数据进行序列化和反序列化。
  • 更容易、更有效地将视图数据相关逻辑从视图控制器(Activity和Fragment)中分离
  • 可以避免内存泄漏。它专门用来管理网络请求等异步调用

ViewModel的创建

一般我们会通过下面这行代码创建或获取ViewModel

ViewModelProvider(this).get(MvvmViewModel::class.java)

ViewModelProvider的参数是一个ViewModelStoreOwner对象,ComponentActivity实现了这个接口,它表明实现类会持有一个ViewModelStore对象。

这行代码的作用主要就是从传入的ViewModelStoreOwner对象中取出想要获取的ViewModel类,或通过反射创建ViewModel对象并将其存储在ViewModelStore的Map中。

ViewModelProvider的构造方法中,都会通过ViewModelStoreOwner的getViewModelStore方法来获取ViewModelStore。

public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
	  this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
          ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
          : NewInstanceFactory.getInstance());
}

public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
    this(owner.getViewModelStore(), factory);
}

Activity重建时如何保证ViewModel不重建

主要是通过**onRetainNonConfigurationInstance**这个方法实现的,通过这个方法可以像onSaveInstanceState()的方法一样保留变化前的Activity State,最大的不同在于这个方法可以返回一个包含有状态信息的Object。

注意的点

1、onRetainNonConfigurationInstance()在onSaveInstanceState()之后被调用。

2、调用顺序同样介于onStop()和onDestroy()之间(onSaveInstanceState在API28以后在onStop之后被调用)。

在ComponentActivity中,这个方法的声明如下:

//注释说:这个方法用来保存所有合适的非配置状态
public final Object onRetainNonConfigurationInstance() {
    Object custom = onRetainCustomNonConfigurationInstance();

    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) {
        // No one called getViewModelStore(), so see if there was an existing
        // ViewModelStore from our last NonConfigurationInstance
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }

    if (viewModelStore == null && custom == null) {
        return null;
    }

    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    return nci;
}

当系统配置发生改变时,Activity会被销毁并进行重建。

当ActivityThread执行到performDestroyActivity()这个方法时,就会调用Activity的retainNonConfigurationInstances()将保存的数据保存到ActivityClientRecord中;

当页面重构完成,就会调用ActivityThread的performLaunchActivity()将这个数据重新赋给Activity的mLastNonConfigurationInstances属性。

在Activity重建时,会再次调用ViewModelProvider(this).get(MvvmViewModel::class.java)这行代码,通过ViewModelStoreOwner的getViewModelStore方法来获取ViewModelStore:

public ViewModelStore getViewModelStore() {
    if (getApplication() == null) {
        throw new IllegalStateException("Your activity is not yet attached to the "
                + "Application instance. You can't request ViewModel before onCreate call.");
    }
    if (mViewModelStore == null) {
			  //获取Activity销毁前ActivityClientRecord中保存的这个对象
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            //直接从NonConfigurationInstances中获取之前的viewModelStore对象,其中的Map存储了ViewModel的引用
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

因此重建后的新Activity持有的仍是原先的ViewModel对象。

ViewModel的销毁

在ComponentActivity的无参构造方法中有下面这些代码:

getLifecycle().addObserver(new LifecycleEventObserver() {
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        if (event == Lifecycle.Event.ON_DESTROY) {
				    //如果因为系统配置更改(旋转)导致销毁Activity,则不会清除ViewModelStore中的ViewModel
            if (!isChangingConfigurations()) {
                getViewModelStore().clear();
            }
        }
    }
});

这里向ComponentActivity中的mLifecycleRegistry注册了一个LifecycleEventObserver,在收到Activity销毁的事件时会调用getViewModelStore().clear()方法将ViewModelStore中的ViewModel全部销毁。

ViewModel如何在Activity和各个Fragment内部共享

如果Activity和其中的几个Fragment需要共享一个ViewModel,我们一般会这样来获取ViewModel:

在Activity中

ViewModelProvider(this).get(MyViewModel::class.java)

//下面通过getViewModelStore方法获取了ViewModelStoreOwner持有的ViewModelStore对象
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
    this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
            ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
            : NewInstanceFactory.getInstance());
}

因为ViewModel的实例是存储在Activity(ViewModelStoreOwner)持有的ViewModelStore中的一个HashMap中的,因此这里是获取HashMap中的ViewModel或反射创建ViewModel并放入HashMap中。

在Fragment中获取:

ViewModelProvider(getActivity()).get(MyViewModel.class);

ViewModelProvider中传入的是Fragment对应的Activity,本质上还是从Activity中持有的ViewModelStore中的一个HashMap中来获取的,因此它们获取的是同一个ViewModel对象,可以进行共享。

总结

ViewModel如何保证在Actvity旋转前后获取到的是同一个?

因为在执行onDestroy之前,从ActivityClientRecord持有一条到ViewModelStore引用链,所以当Activity被销毁时,ViewModelStore不会被垃圾回收,也就不会被销毁,而Activity的reLaunch并不会销毁对应的ActivityClientRecord,下次仍然会复用ActivityClientRecord,进而复用保存的ViewModelStore,这样就解释通了;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值