Jetpack介绍之ViewModel

1.介绍

ActivityFragment界面控制器是Android系统提供给我们用的类,可能会根据当前的情况,随时销毁或者重新创建页面控制器,这样就需要我们对于当前界面的数据要做合理的保存,以便发生这种情况可以恢复,ActivityFragment应该只做UI数据的更新,页面数据的提供应该拆分给一个单独的类来做,就是ViewModel

2.使用

从整体看一下:
在这里插入图片描述

一个ActivityFragment可以对应一个或者多个ViewModel,用来提供数据

在这里插入图片描述

ViewModel不应该绑定View视图相关,Lifecycle生命周期,Activity,或者Context这些Android相关的类,数据改变通知UI进行更新都需要使用LiveData来做;
下图是ViewModel的生命周期
在这里插入图片描述

3.源码分析

在这里插入图片描述

我们将测试APP运行之后,前三行的打印,然后再将屏幕进行旋转,再看一下相同的打印,从结果上我们可以看出来,只有ViewModel是相同的,其他的FragmentActivity都是新的对象。

这样我们就来分析一下,为什么需要这种方式来新建ViewModel

在这里插入图片描述

以上就是整体的初始化过程,我们具体对照源码分析一下

我们来看一下是如何创建的ViewModel

mViewModel = new ViewModelProvider(this).get(MainViewModel.class);
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore()/*fragment 或者 activity会提供对应的ViewModelStore*/, owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
    @NonNull
    @Override
    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        if (mFragmentManager == null) {
            throw new IllegalStateException("Can't access ViewModels from detached fragment");
        }
        //第一次的情况下为null,如果旋转屏幕则不为空直接返回
        if (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    requireActivity().getApplication(),
                    this,
                    getArguments());
        }
        return mDefaultFactory;
    }

真正创建ViewModel的地方是在SavedStateViewModelFactory中的,那这个类干什么的呢?

{@link androidx.lifecycle.ViewModelProvider.Factory} that can create ViewModels accessing and
 * contributing to a saved state via {@link SavedStateHandle} received in a constructor.
 * If {@code defaultArgs} bundle was passed into the constructor, it will provide default
 * values in {@code SavedStateHandle}.

通过查看注释我们可以知道它能创建一个包含SavedStateHandle的构造函数,这样创建流程就结束了,我们看到就是将几个类进行了初始化,应该和配置没有关系,那么具体的实现就应该在get方法里面,我们再接着来看:

    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);
    }
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        //1.使用了一个独一无二的名字,同创建的类名相关,ViewModelStore是一个hashmap,保存当前实例化的ViewModel类,这样就算是屏幕旋转也会保存在内存中了,只要在activity合适的时机上进行销毁即可。
        ViewModel viewModel = mViewModelStore.get(key);

        //2.第一次这个ViewModel为空,只有后续不为空的时候会走到这里面,执行SavedStateViewModelFactory中的onRequery,会将SavedStateHandleController将lifecycle进行关联(SavedStateHandleController使用来处理SavedStateHandle的)
        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.
            }
        }
        //自定义factory实现了此接口,我们看默认的就好了
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            //会在此进行创建,调用SavedStateViewModelFactory的create函数进行创建的
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }
    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 {
            //普通的ViewModel直接看这里,构造函数是否有SavedStateHandle,便于后续反射实例化
            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);
            //返回viewModel
            return viewmodel;
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Failed to access " + modelClass, e);
        } catch (InstantiationException e) {
            throw new RuntimeException("A " + modelClass + " cannot be instantiated.", e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException("An exception happened in constructor of "
                    + modelClass, e.getCause());
        }
    }

这样整理流程就梳理完毕了,我们知道了保存到了HashMap中,只要一直有这个HashMap那么就可以获取到ViewModel的对象了,那我们接着来看一下屏幕发生旋转变化是由哪里触发的吧,我们知道旋转屏幕的话会将屏幕先destorycreate,那我么看一下如果HashMapActivity实例有关系按理说destory的时候也会将HashMap给清除啊,再看一下代码:

    void destroy(@NonNull FragmentHostCallback<?> host,
            @NonNull FragmentManagerViewModel nonConfig) {
        if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
            Log.d(TAG, "movefrom CREATED: " + mFragment);
        }
        //false
        boolean beingRemoved = mFragment.mRemoving && !mFragment.isInBackStack();
        //true
        boolean shouldDestroy = beingRemoved || nonConfig.shouldDestroy(mFragment);
        if (shouldDestroy) {
            boolean shouldClear;
            if (host instanceof ViewModelStoreOwner) {
                //如果是ViewModelStoreOwner那么为false
                shouldClear = nonConfig.isCleared();
            } else if (host.getContext() instanceof Activity) {
                Activity activity = (Activity) host.getContext();
                shouldClear = !activity.isChangingConfigurations();
            } else {
                shouldClear = true;
            }
            //旋转屏幕不会走到这里来
            if (beingRemoved || shouldClear) {
                如果走到这里的话最终会调用到ViewModeStore的clear方法的,
                nonConfig.clearNonConfigState(mFragment);
            }
            mFragment.performDestroy();
            mDispatcher.dispatchOnFragmentDestroyed(mFragment, false);
        } else {
            mFragment.mState = Fragment.ATTACHED;
        }
    }

这样我们就可以看到了,正常的Destory是会清除ViewModelStore的,但是如果是屏幕旋转的时候做了特殊的处理所以还有保存的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值