美文网首页
解析ViewModel

解析ViewModel

作者: Merlin_720 | 来源:发表于2019-05-29 23:33 被阅读0次

    1.用法

    我们先看一下VIewModel的简单用法

     MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
    

    简单的一句话就可以在activity或者fragment里获取VIewModel对象,然后可以对他执行操作。然后通过注册观察者通知ui组件更新内容

     model.getUsers().observe(this, users -> {
                // update UI
            });
    

    2.应用场景

    • 1.恢复re_create前的数据。
    • 2.维护异步回调
    • 3.简单的ui组件。

    3.大致流程以及源码解析

    流程图.png

    既然大家知道VIewModel的用法,那么它的内部实现是什么样的,这里我先说一下大致的流程,就是ViewModelProviders通过of方法获取ViewModelProvider对象,然后通过ViewModelProvider的get方法获取到的ViewModel对象。既然大家知道大致的流程,那边下边我们来看一下具体的代码实现吧。
    首先看一下ViewModelProvider的of方法,看看他是如何创建ViewModelProvider的。

     /**
         * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given Activity
         * is alive. More detailed explanation is in {@link ViewModel}.
         * <p>
         * It uses the given {@link Factory} to instantiate new ViewModels.
         *
         * @param activity an activity, in whose scope ViewModels should be retained
         * @param factory  a {@code Factory} to instantiate new ViewModels
         * @return a ViewModelProvider instance
         */
        @NonNull
        @MainThread
        public static ViewModelProvider of(@NonNull FragmentActivity activity,
                @Nullable Factory factory) {
            Application application = checkApplication(activity);
            if (factory == null) {
                factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
            }
            return new ViewModelProvider(ViewModelStores.of(activity), factory);
        }
    

    中间的方法是为了适配非v4包下的activity或者fragment,所以先不看那个方法,先看一下最后一句。new了一个ViewModelProvider对象,需要两个参数,一个ViewModelStore和一个factory。第二个参数很简单就是一个工厂创建出来的。那么接下来看第一个参数ViewModelStore是如何创建的。

     /**
         * Returns the {@link ViewModelStore} of the given activity.
         *
         * @param activity an activity whose {@code ViewModelStore} is requested
         * @return a {@code ViewModelStore}
         */
        @NonNull
        @MainThread
        public static ViewModelStore of(@NonNull FragmentActivity activity) {
            if (activity instanceof ViewModelStoreOwner) {
                return ((ViewModelStoreOwner) activity).getViewModelStore();
            }
            return holderFragmentFor(activity).getViewModelStore();
        }
    

    这里我们还是先跳过中间的部分,还是适配非v4包下的activity和fragment,直接看最后的return,这里调用了一个静态方法holderFragmentFor,我们进入到这个方法去看看他执行了什么操作。

     /**
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public static HolderFragment holderFragmentFor(FragmentActivity activity) {
            return sHolderFragmentManager.holderFragmentFor(activity);
        }
    

    这里返回了HolderFragment,这个是我们的重点,待会我们说他。这个HolderFragment内部有一个成员变量,直接返回了ViewModelStore,下边是源码。

    public class HolderFragment extends Fragment implements ViewModelStoreOwner {
        private ViewModelStore mViewModelStore = new ViewModelStore();
      @NonNull
        @Override
        public ViewModelStore getViewModelStore() {
            return mViewModelStore;
        }
    }
    

    这里省去了部分代码,这里我们找到了ViewModelStore是如何创建的。知道了如何创建ViewModeStore,我们来看看它做了什么

    /**
     * Class to store {@code ViewModels}.
     * <p>
     * An instance of {@code ViewModelStore} must be retained through configuration changes:
     * if an owner of this {@code ViewModelStore} is destroyed and recreated due to configuration
     * changes, new instance of an owner should still have the same old instance of
     * {@code ViewModelStore}.
     * <p>
     * If an owner of this {@code ViewModelStore} is destroyed and is not going to be recreated,
     * then it should call {@link #clear()} on this {@code ViewModelStore}, so {@code ViewModels} would
     * be notified that they are no longer used.
     * <p>
     * {@link android.arch.lifecycle.ViewModelStores} provides a {@code ViewModelStore} for
     * activities and fragments.
     */
    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);
        }
    
        /**
         *  Clears internal storage and notifies ViewModels that they are no longer used.
         */
        public final void clear() {
            for (ViewModel vm : mMap.values()) {
                vm.onCleared();
            }
            mMap.clear();
        }
    }
    

    ViewModelProvider整个创建过程已经介绍完了,下边我们来看看他的get方法是如何获取ViewModel对象的,在看get方法之前先看一下ViewModelStore

    /**
     * Class to store {@code ViewModels}.
     * <p>
     * An instance of {@code ViewModelStore} must be retained through configuration changes:
     * if an owner of this {@code ViewModelStore} is destroyed and recreated due to configuration
     * changes, new instance of an owner should still have the same old instance of
     * {@code ViewModelStore}.
     * <p>
     * If an owner of this {@code ViewModelStore} is destroyed and is not going to be recreated,
     * then it should call {@link #clear()} on this {@code ViewModelStore}, so {@code ViewModels} would
     * be notified that they are no longer used.
     * <p>
     * {@link android.arch.lifecycle.ViewModelStores} provides a {@code ViewModelStore} for
     * activities and fragments.
     */
    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);
        }
    
        /**
         *  Clears internal storage and notifies ViewModels that they are no longer used.
         */
        public final void clear() {
            for (ViewModel vm : mMap.values()) {
                vm.onCleared();
            }
            mMap.clear();
        }
    }
    

    ViewModelStore很简单内部维护一个HashMap,提供三个方法,存、取和清空。

    public class ViewModelProvider {
    /**
         * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
         * an activity), associated with this {@code ViewModelProvider}.
         * <p>
         * The created ViewModel is associated with the given scope and will be retained
         * as long as the scope is alive (e.g. if it is an activity, until it is
         * finished or process is killed).
         *
         * @param modelClass The class of the ViewModel to create an instance of it if it is not
         *                   present.
         * @param <T>        The type parameter for the ViewModel.
         * @return A ViewModel that is an instance of the given type {@code T}.
         */
        @NonNull
        @MainThread
        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);
        }
    
    /**
         * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
         * an activity), associated with this {@code ViewModelProvider}.
         * <p>
         * The created ViewModel is associated with the given scope and will be retained
         * as long as the scope is alive (e.g. if it is an activity, until it is
         * finished or process is killed).
         *
         * @param key        The key to use to identify the ViewModel.
         * @param modelClass The class of the ViewModel to create an instance of it if it is not
         *                   present.
         * @param <T>        The type parameter for the ViewModel.
         * @return A ViewModel that is an instance of the given type {@code T}.
         */
        @NonNull
        @MainThread
        public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
            ViewModel viewModel = mViewModelStore.get(key);
    
            if (modelClass.isInstance(viewModel)) {
                //noinspection unchecked
                return (T) viewModel;
            } else {
                //noinspection StatementWithEmptyBody
                if (viewModel != null) {
                    // TODO: log a warning.
                }
            }
    
            viewModel = mFactory.create(modelClass);
            mViewModelStore.put(key, viewModel);
            //noinspection unchecked
            return (T) viewModel;
        }
    }
    

    ViewModelProvider先创建一个有规律的key,然后通过这个这个可以去ViewModelStore中取,如果匹配我们的viewModel那么就返回,如果不匹配,就通过工厂创建一个新的Viewmodel,然后存到ViewModelStore中,然后返回ViewModel对象。这样整个获取ViewModel对象的过程就分析完了,下边我们来看看那个HolderFragment,在这里我们看到ViewModel是如何销毁的。

    4.ViewModel的死亡

    public abstract class ViewModel {
        /**
         * This method will be called when this ViewModel is no longer used and will be destroyed.
         * <p>
         * It is useful when ViewModel observes some data and you need to clear this subscription to
         * prevent a leak of this ViewModel.
         */
        @SuppressWarnings("WeakerAccess")
        protected void onCleared() {
        }
    }
    

    Vm 只有一个onCleared方法,那么他是在何时调用呢?这里就要看之前提到的HolderFragment

    public class HolderFragment extends Fragment {
        private static final String LOG_TAG = "ViewModelStores";
    
        private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();
    
        /**
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public static final String HOLDER_TAG =
                "android.arch.lifecycle.state.StateProviderHolderFragment";
    
        private ViewModelStore mViewModelStore = new ViewModelStore();
    
        public HolderFragment() {
            setRetainInstance(true);
        }
    
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            sHolderFragmentManager.holderFragmentCreated(this);
        }
    
       @Override
        public void onDestroy() {
            super.onDestroy();
            mViewModelStore.clear();
        }
    
        public ViewModelStore getViewModelStore() {
            return mViewModelStore;
        }
    
        static class HolderFragmentManager {
            private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
            private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>();
            ……
             private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
                HolderFragment holder = new HolderFragment();
                fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
                return holder;
            }
             HolderFragment holderFragmentFor(FragmentActivity activity) {
                FragmentManager fm = activity.getSupportFragmentManager();
                HolderFragment holder = findHolderFragment(fm);
                if (holder != null) {
                    return holder;
                }
                holder = mNotCommittedActivityHolders.get(activity);
                if (holder != null) {
                    return holder;
                }
    
                if (!mActivityCallbacksIsAdded) {
                    mActivityCallbacksIsAdded = true;
                    activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
                }
                holder = createHolderFragment(fm);
                mNotCommittedActivityHolders.put(activity, holder);
                return holder;
            }
      ……
    }
    

    Vm 创建的时候提到过 实例化了一个 HolderFragment 。并且实例化的时候通过上面createHolderFragment 方法将其fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
    我们知道commit 之后 fragment 将会拥有灵魂,获得生命周期。再看其onDestroy方法里
    调用了 mViewModelStore.clear();

    5.那么为什么旋转屏幕为什么不会onClear

    因为在HolderFragment的构造方法里调用了setRetainInstance(true);
    这一节先介绍到这,下边会跟进为什么VIewModel会和activity声明周期结合,并且如何把数据更新的。

    公众号

    相关文章

      网友评论

          本文标题:解析ViewModel

          本文链接:https://www.haomeiwen.com/subject/qjrptctx.html