JetPack之ViewModel的几点原理分析


ViewModel有两个显著的优点:

  1. 同一个Activity下的不同Fragment之间可以用ViewModel通信;
  2. ViewModel的生命周期极长,即使其LifecycleOwer销毁了ViewModel也依旧存活;
    第一个优点得益于ViewModel的组织架构,Activity和Fragment会缓存一个ViewModel集合,每一个ViewMode都是单例,以其Class名为key,以ViewMode实例为value存储在一个HashMap中。ViewModelStore是用于存储ViewModel的,Factory是用于生产ViewModel的,ComponentActivity中有一个ViewModelStore对象和一个Factory。每次要获取一个ViewModel时,就将ComponentActivity中的ViewModelStore和Factory对象组装为一个ViewModelProvider对象,该对象会对ViewModel进行管理和维护。
    在这里插入图片描述

1. ViewModel的存取设计

1.1. ViewModelStore的提供

ViewModel模块中定义了ViewModelStoreOwner接口来表示ViewModelStore拥有者,该拥有者自然应该定义获取ViewModelStore的方法getViewModelStore()。ComponentActivity和Fragment都实现了该接口,ComponentActivity中的getViewModelStore()相对简单,即是会返回一个ViewModelStore单例。而Fragment中的实现相对复杂,是交给FragmentManager类的类型为FragmentManagerViewModel的成员变量mNonConfig来处理的,FragmentManagerViewModel继承与ViewModel。

//FragmentManagerViewModel.java
ViewModelStore getViewModelStore(@NonNull Fragment f) {
        ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
        if (viewModelStore == null) {
            viewModelStore = new ViewModelStore();
            mViewModelStores.put(f.mWho, viewModelStore);
        }
        return viewModelStore;
    }

FragmentManagerViewModel的mViewModelStores成员变量是以Fragment.mWho为key,ViewModelStore对象为value的HashMap。FragmentManager管理许多个Fragment和每个Fragment对应的ViewModelStore,FragmentManager将这些信息存储在FragmentManagerViewModel中的mRetainedFragments和mViewModelStores中。

1.2. Factory的提供

获取一个ViewModel时,我们往往不需要自己传一个Factory对象,往往ViewModelStoreOwner都实现了默认的Factory提供方法。ViewModel模块中定义了HasDefaultViewModelProviderFactory接口来标示一个ViewModelStoreOwner有默认的Factory提供方案。ComponentActivity和Fragment都实现了HasDefaultViewModelProviderFactory接口并且实现的Factory提供方法是基本一致的,都是返回一个SavedStateViewModelFactory对象,SavedStateViewModelFactory具有状态保存功能,通过SavedStateHandle实现。ViewModelProvider中有三类Factory:

  1. 虚类KeyedFactory,其creat()方法中有一个String类型的key参数,SavedStateViewModelFactory是其实现类
  2. NewInstanceFactory,采用零参构造器反射生成ViewModel对象
  3. AndroidViewModelFactory,采用零参构造器反射生成AndroidViewModel对象

1.3 ViewModel的存取

获取一个ViewModel是通过调用ViewModelProvider中的get()方法,其逻辑是先从ViewModelStore中获取缓存的ViewModel,若没有缓存着的ViewModel,则采用Factory生产一个ViewModel,看一下ComponentActivity和Fragement默认提供的SavedStateViewModelFactory类的create()方法的实现:

public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
        boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
        Constructor<T> constructor;
        if (isAndroidViewModel) {
            constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
        } else {
            constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
        }
        // doesn't need SavedStateHandle
        if (constructor == null) {
            return mFactory.create(modelClass);
        }

        SavedStateHandleController controller = SavedStateHandleController.create(
                mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
        try {
            T viewmodel;
            if (isAndroidViewModel) {
                viewmodel = constructor.newInstance(mApplication, controller.getHandle());
            } else {
                viewmodel = constructor.newInstance(controller.getHandle());
            }
            viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
            return viewmodel;
        }

主要逻辑是通过指定的构造器参数查找合适的构造器,并反射生成相应的ViewModel对象或AndroidViewModel对象。指定的构造区参数定义为:

private static final Class<?>[] ANDROID_VIEWMODEL_SIGNATURE = new Class[]{Application.class,
            SavedStateHandle.class};
    private static final Class<?>[] VIEWMODEL_SIGNATURE = new Class[]{SavedStateHandle.class};

从这里可知当ViewMode有定义含SavedStateHandle类型的参数的构造器中,则该ViewModel具有保存状态功能。如果没有找到指定参数的构造器,则会采用mFactory来生产ViewModel,mFactory的类型是AndroidViewModelFactory,AndroidViewModelFactory继承于NewInstanceFactory,AndroidViewModelFactory的creat()的逻辑是先试着创建一个AndroidViewModel对象,若创建不成功则采用父类的creat()方法创建一个ViewModel对象。

2. SavedStateHandle保存数据原理

要使用SavedStateHandle,需要引入下面的依赖:

androidx.lifecycle:lifecycle-viewmodel-savedstate:$savedStateVersion

通过上一节可以知道在通过反射构造器初始化一个ViewModel实例时,会传入一个SavedStateHandle对象,这个对象是调用SavedStateHandleController的getHandle()方法获取的,SavedStateHandle内部通过类型为HashMap的mRegular来保存变量,与SavedStateHandle相关的类的关系图如下所示:
在这里插入图片描述
简述这几个类的作用:

  1. SavedStateHandle用于保存变量,存储在HashMap类型的mRegular中;
  2. SavedStateHandleCntroller用于管理SavedStateHandle;
  3. SavedStateRegistry:用于消费和生产保存变量数据,SavedStateHandle可以向SavedStateRegistry注册保存变量提供者对象SavedStateProvider;
  4. SavedStateRegistryController:用于管理SavedStateRegistry;
  5. SavedStateProvider:保存状态提供者,用于将SavedStateHandle的保存状态提供给SavedStateRegistry,并序列化保存到Bundle中;

2.1. 状态保存过程

下面看一下保存数据过程。假设横竖屏切换,这时会调用ComponentActivity的onSaveInstanceState(),接着会调用SavedStateRegistryController的performSave()方法,最终会调用到SavedStateRegistry的performSave()方法:

void performSave(@NonNull Bundle outBundle) {
        Bundle components = new Bundle();
        if (mRestoredState != null) {
            components.putAll(mRestoredState);
        }
        for (Iterator<Map.Entry<String, SavedStateProvider>> it =
                mComponents.iteratorWithAdditions(); it.hasNext(); ) {
            Map.Entry<String, SavedStateProvider> entry1 = it.next();
            components.putBundle(entry1.getKey(), entry1.getValue().saveState());
        }
        outBundle.putBundle(SAVED_COMPONENTS_KEY, components);
    }

mComponents是以map形式保存注册进来的SavedStateProvider对象的,调用SavedStateProvider的saveState()方法即可将SavedStateHandle中的数据保存在Bundle对象中返回,最后保存在key=SAVED_COMPONENTS_KEY的Bundle中。

2.2. 状态恢复过程

当调用ComponentActivity的onCreate()方法时,最终会调用到SavedStateRegistry的performRestore()方法:

//SavedStateRegistry.java
void performRestore(@NonNull Lifecycle lifecycle, @Nullable Bundle savedState) {
        if (mRestored) {
            throw new IllegalStateException("SavedStateRegistry was already restored.");
        }
        if (savedState != null) {
            mRestoredState = savedState.getBundle(SAVED_COMPONENTS_KEY);
        }
        mRestored = true;
    }

从外面传入的Bundle中获取key=SAVED_COMPONENTS_KEY的类型为Bundle的对象并保存在mRestoredState成员变量中。当创建一个SavedStateViewModelFactory对象时会调用SavedStateHandleController的create()方法:

static SavedStateHandleController create(SavedStateRegistry registry, Lifecycle lifecycle,
            String key, Bundle defaultArgs) {
        //从SavedStateRegistry对象中获取恢复状态信息
        Bundle restoredState = registry.consumeRestoredStateForKey(key);
        //以恢复的restoredState信息重新创建一个SavedStateHandle对象,虽然是不同的SavedStateHandle对象,但是其内部数据应该和以前一样
        SavedStateHandle handle = SavedStateHandle.createHandle(restoredState, defaultArgs);
        SavedStateHandleController controller = new SavedStateHandleController(key, handle);
        controller.attachToLifecycle(registry, lifecycle);
        tryToAddRecreator(registry, lifecycle);
        return controller;
    }

又回到SavedStateRegistry中,通过consumeRestoredStateForKey()方法返回key对应的restoredState值,看一下这个方法的实现:

public Bundle consumeRestoredStateForKey(@NonNull String key) {
        if (!mRestored) {
            throw new IllegalStateException("You can consumeRestoredStateForKey "
                    + "only after super.onCreate of corresponding component");
        }
        if (mRestoredState != null) {
            Bundle result = mRestoredState.getBundle(key);
            mRestoredState.remove(key);
            if (mRestoredState.isEmpty()) {
                mRestoredState = null;
            }
            return result;
        }
        return null;
    }

这里的mRestoredState即是在performRestore()方法中恢复并保存的值。

3. ViewModel的超长生命周期原理

当配置发生变化时(横竖屏切换),Activiry会先Destory然后会创建一个新的Activity实例,然而新Activity中的ViewModelStore实例却没有重新创建,因此ViewModel也不会重新创建了,引用官方的图可以清晰展示:
在这里插入图片描述
这与Activity的onSaveInstance()/onRestoreInstance()的原理是一致的。下面看一下横竖屏切换的部分时序图:

Activity ActivityThread TransactionExecutor ActivityRelaunchItem scheduleRelaunchActivity() 使用Handler机制发送一个RELAUNCH_ACTIVITY消息 executeCallbacks() execute() handleRelaunchActivity() handleDestroyActivity() performDestroyActivity() performDestroyActivity()方法会调用当前Activity中的 retainNonConfigurationInstances()获得一个 NonConfigurationInstances对象并缓存在ActivityClientRecord 对象的lastNonConfigurationInstances变量中 handleLaunchActivity() attatch() attatch()函数的参数lastNonConfigurationInstances传入的值为缓存在 ActivityClientRecord中的lastNonConfigurationInstances Activity ActivityThread TransactionExecutor ActivityRelaunchItem

这里Activity的lastNonConfigurationInstances可以缓存ViewModelStore的,在performDestroyActivity()方法中会缓存一些数据到ActivityClientRecord中:

ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
        ActivityClientRecord r = mActivities.get(token);
            if (getNonConfigInstance) {
                try {
                    r.lastNonConfigurationInstances
                            = r.activity.retainNonConfigurationInstances();
                } 
            try {
                r.activity.mCalled = false;
                mInstrumentation.callActivityOnDestroy(r.activity);              
            }
            r.setState(ON_DESTROY);
        }
        return r;
    }

如果getNonConfigInstance为真则在Activity销毁时会缓存lastNonConfigurationInstances到ActivityClientRecord中,Activity中的retainNonConfigurationInstances()方法最终会调用到onRetainNonConfigurationInstance()方法:

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中的viewModelStore对象缓存在一个在NonConfigurationInstances对象中的viewModelStore中。ComponentActivity中获取ViewModelStore是会先从lastNonConfigurationInstances来获取,若不成功,则创建一个新的ViewModelStore实例:

public ViewModelStore getViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

通过以上分析可以看出缓存实现的原理: 将要缓存的数据在Activity销毁的时候缓存在ActivityClientRecord中,当屏幕切换时虽然Activity会销毁,但是ActivityClientRecord不会销毁,因此缓存得以保存。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值