JetPack之ViewModel最新源码详细分析

本文会基于最新版ViewModel使用方法与源码进行详细分析,从注册到实现ViewModel界面数据如何保存与管理全部涉及。

**

简介:

**ViewModel是JetPack系列库之一,它用来对组件的界面数据进行管理,且当组件的状态发生改变时数据依然留存。

优点:1.当所依赖组件的状态发生改变时,例如屏幕旋转等,界面数据不会发生改变
2.实现MVVM架构的基础,在日常开发中,ViewModel就是MVVM架构的VM层,它相当于MVP架构的present,我们可以将业务逻辑以及数据交互的部分放入ViewModel中。

源码分析:

版本:viewmodel:1.1.1
此处使用的示例代码与livedata源码分析的相同,不影响流程分析

首先构造一个继承于ViewModel的类
在这里插入图片描述
接着创建一个factory 实例传入ViewModelProvider(this,factory )中去并调用get方法将我们的ViewModel作为参数传入。
此处需要注意的是在以前的版本中直接通过 ViewModelProviders.of(this).get()方法来完成这个操作的。在最新的版本中已经取消了of方法,并且构造方法必须要传入2个参数
在这里插入图片描述
查看ViewModelProvider的构造方法可看出构造ViewModelProvider时必须传入2个参数才行,其中第一个参数为ViewModelStore,第二个参数为factory,
ViewModelStore用来存储和管理ViewModelfactory则用来对ViewModel进行实例化。此时构建ViewModelProvider时传入的参数为所依赖的组件的ViewModelStore,也就是说,我们通过组件的ViewModelStore来对ViewModel进行存储和管理。
在这里插入图片描述

其中factory需要我们在构造的时候进行传入,ViewModelProvider给我们提供了3种实例化的方法,分别是简单工厂模式构造,通过反射构造实例,以及我们自己实现factory构造实例。

当我们使用ViewModelProvider.NewInstanceFactory()构造实例时,查看源码可看出,它实现了Factory 接口,并且在其内部的create方法中会调用我们在之前get方法中传入的viewmodelnewInstance方法。这个方法需我们自己在viewmodel中去实现,用来返回viewmodel实例,如果实例化出错则会抛出异常

   public static class NewInstanceFactory implements Factory {

        @SuppressWarnings("ClassNewInstance")
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            //noinspection TryWithIdenticalCatches
            try {
            //返回modelClass实例对象
                return modelClass.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
    }

使用AndroidViewModelFactory 方式反射生成ViewModel,该Factory使用单例模式进行构建 ,所以它的生命周期等同于应用程序的生命周期,且只有一个实例,推荐使用此模式来获取factory,当它的create方法被调用时,会通过application利用反射创建一个ViewModel实例并返回。

  public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

        private static AndroidViewModelFactory sInstance;
        @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

        private Application mApplication;

        public AndroidViewModelFactory(@NonNull Application application) {
        //构造时进行传入
            mApplication = application;
        }

        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                //noinspection TryWithIdenticalCatches
                try {
                //通过反射创建viewmodel
                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InstantiationException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                }
            }
            return super.create(modelClass);
        }
    }

第三种方式就是直接实现Factory接口,并自己实现create方法,我们需要在该create方法内,返回viewmodel的实例

  /**
     * Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
     */
    public interface Factory {
        /**
         * Creates a new instance of the given {@code Class}.
         * <p>
         *
         * @param modelClass a {@code Class} whose instance is requested
         * @param <T>        The type parameter for the ViewModel.
         * @return a newly created ViewModel
         */
        @NonNull
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    }

当我们在构造ViewModelProvider时,会将我们传入的这2个参数进行保存,

   public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }

当我们调用get方法时

    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
    //获取到ViewModel的类名
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        //再次进行回调
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }

在该方法内首先会从ViewModelStore中通过key去获取ViewModel ,这个key就是上面的DEFAULT_KEY +类名。如果找到则直接返回,否则调用factorycreate方法创建ViewModel实例放入ViewModelStore进行管理和保存并返回该实例

    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    //从ViewModelStore中获取viewModel 
        ViewModel viewModel = mViewModelStore.get(key);
	//如果找到则直接返回
        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            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);
        //noinspection unchecked
        return (T) viewModel;
    }

ViewModelStore 内部通过hashmap来对viewmodel进行管理,

public class ViewModelStore {

    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();
    }
}

到此处为止源码部分分析完毕,如何触发viewmodel对组件界面的状态保存继续分析

上面说过在构建ViewModelProvider时传入的ViewModelStore来自于构建它所传入的this,也就是当前组件,我们知道ViewModelStore起到了对viewmodel的管理作用,viewmodel的获取和存储都是通过它来完成。

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
    在这里调用了组件的owner.getViewModelStore()方法
        this(owner.getViewModelStore(), factory);
    }

追踪getViewModelStore方法,发现它是一个ViewModelStoreOwner 接口中的一个方法

public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}

这里发现它的实现类有很多个,我们一般在ViewModelProvider中传入的是fragment或者activity,而fragment其实最终也是调用的activity的getViewModelStore方法这里查看activity如何处理。
在这里插入图片描述

发现在activity的父类ComponentActivity中有这个方法的实现,通过getViewModelStore去获取一个ViewModelStore ,当ViewModelStore 获取为空时,会调用getLastNonConfigurationInstance方法去恢复状态改变前的NonConfigurationInstances,和这个方法对应的是onRetainNonConfigurationInstance,该方法会在组件状态改变时去保存数据信息,由系统调用

   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.");
        }
        //如果当前组件的mViewModelStore 为空,则从NonConfigurationInstances 中去获取
        if (mViewModelStore == null) {
        //返回之前
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            //如果在NonConfigurationInstances 中没有找到,则创建一个新的ViewModelStore并保存起来
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

在代码中ViewModelStore会从NonConfigurationInstances 中去尝试获取,我们查看viewModelStore的赋值时机即可

  static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }

查看发现它的赋值时机就是在onRetainNonConfigurationInstance中完成的,也就是说在组件的状态发生改变时,会去尝试获取viewModelStore,并将viewModelStore保存在NonConfigurationInstances 中并返回。
也就是在onRetainNonConfigurationInstance中会对viewModelStore进行保存,在getLastNonConfigurationInstance中会对viewModelStore进行恢复
在这里插入图片描述
具体流程:
当我们在activity中去实例化ViewModelProvider时,所依赖的activity在其内部会调用getViewModelStore方法去获取一个ViewModelStore,如果没有则创建一个,并保存起来,然后调用ViewModelProvider.get方法将我们的viewmodel实例通过ViewModelStore进行保存管理,当我们的activity的状态发生改变,如旋转屏幕,此时系统会调用onRetainNonConfigurationInstance方法,在这个方法内会将我们的ViewModelStore进行保存。

我们一般会搭配livedata来使用viewmodellivedata常用来存储与界面相关的数据元素,当我们的activity状态改变后,界面再次从livedata中获取界面数据时,由于livedata实例由viewmodel保管,而viewmodel存储在ViewModelStore中,当前activity去获取ViewModelStore时会通过getLastNonConfigurationInstance方法恢复之前的ViewModelStore,所以状态改变前后的ViewModelStore是同一个,所以数据没有发生任何改变,从而界面相关数据不会发生丢失。

猜你喜欢

转载自blog.csdn.net/qq_24856205/article/details/106922328