Android架构组件三 Android Architecture Components ViewModel组件解析

1 前言

ViewModel是android架构组件中非常重要的一个组件,它是Android架构分层的核心,有关它的用法和资料可以参考
Android架构组件一 Android Architecture Components开发入门
也可以参考官方给出的示例https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html
接下来我们从以下几个方面介绍ViewModel组件

2 ViewModel的作用

ViewModel组件的官方定义如下:
The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.

翻译过来就是ViewModel是存储和管理lifecycle 重新创建的数据的组件,在lifecycle 在配置改变或者屏幕旋转时数据仍然在生存周期内。另外ViewModel还可以用来负责UI组件间的通信,它是解耦Activity/Fragment View层的关键。

ViewModel在Android架构组件中的位置如下:
这里写图片描述
可以看到ViewModel处于Activity(View)和Repository(Model)层之间,它类似于MVP中的P层,向下对View层提供操作Model层的接口,向上使用观察者模式观察Model层,当Model层感兴趣的数据发生变化时通知View层,当然这个是ViewModel中引用的LiveData组件来实现的,这个将会在下面一篇博文中来讲解。
ViewModel组件有非常大的一个意义就是它的实例不会因为Activity/Fragment因为配置发生改变或者屏幕旋转时重新创建ViewModel对象。这个是ViewModel区别与MVP中P的基本特点之一。官方给出了如下的图示说明ViewModel的生命周期。
这里写图片描述
可以看到,只有当Activity的finish()方法被调用时,ViewModel.onCleared()方法会被调用,对象才会被销毁。这张图很好的描述了是当Activity被recreate时,ViewModel的生命周期。关于这一点是如何做到的,参考下面的讲解。

注意:*在ViewModel中不要持有Activity等View层的引用,如果需要引用Context,请使用AndroidViewModel,它引用了Application Context*
官方说明如下:
Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context

3 ViewModel UML类图

查看Android架构组件的源码,我们可以得到如下的UML类图。
这里写图片描述

这里有几个比较重要的类
ViewModelProviders:创建ViewModelProvider的工具类,提供以下四个方法创建ViewModelProvider

public static ViewModelProvider of(@NonNull Fragment fragment)
public static ViewModelProvider of(@NonNull FragmentActivity activity)
public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory)
public static ViewModelProvider of(@NonNull FragmentActivity activity,@NonNull Factory factory)

创建时可以指定创建的Factory ,默认已经有一个Factory,定义如下:

public static class DefaultFactory extends ViewModelProvider.NewInstanceFactory {

        private Application mApplication;

        /**
         * Creates a {@code DefaultFactory}
         *
         * @param application an application to pass in {@link AndroidViewModel}
         */
        public DefaultFactory(@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 {
                    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);
        }
    }

重写了public < T extends ViewModel > T create(@NonNull Class < T > modelClass)创建ViewModel对象。

ViewModelProvider: 真正创建和存储ViewModel的类,将创建的ViewModel保存在ViewModelStore 对象mViewModelStore中
创建逻辑如下:

@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;
    }

ViewModelStores:创建ViewModelStore的工具类,关联着HoldFragment ,并返回HolderFragment中的ViewModelStore对象
ViewModelStore: 保存和管理ViewModel的类,里面引用一个HashMap来保存ViewModel对象。并在HolderFragment销毁时清理掉所保存的ViewModel对象。

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.get(key);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
        mMap.put(key, viewModel);
    }

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

HolderFragment:继承自Fragment,是ViewModel在配置改变不被销毁的秘密所在,它主要为ViewModelProvider创建ViewModel提供所需要的ViewModelStore。
在ViewModelStores中有如下关键代码:

    public static ViewModelStore of(@NonNull FragmentActivity activity) {
        return holderFragmentFor(activity).getViewModelStore();
    }

这里的holderFragmentFor就是HolderFragment中的方法,定义如下:

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

    ....
    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;
        }

有关这些类的详细实现请参考源码。

4 ViewModel解析

1 ViewModel实例的创建过程
ViewModel实例的创建过程比较复杂,先经过ViewModelProviders类,再经过ViewModelStores,在跳转到HolderFragment得到ViewModelStore对象,最终跳转到ViewModelProvider的public < T extends ViewModel > T get(@NonNull String key, @NonNull Class< T> modelClass)方法中创建的。整个流程如下:
这里写图片描述
因此,这里我们最终来看ViewModelProvider的public < T extends ViewModel > T get(@NonNull String key, @NonNull Class< T> modelClass)方法

@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;
    }

主要做了以下几件事情
(1) 从mViewModelStore中获取ViewModel对象,mViewModelStore是一个ViewModelStore对象,在创建ViewModelProvider传递过来的,第一次肯定获取为NULL。
(2) 判断是否是ViewModel的实例对象,如果是直接返回,当获取的不为NULL时,且传递的Class对象正确,就就可以在这里直接返回了,说明VikewModel对象之前创立过
(3) 调用mFactory的< T extends ViewModel > T create(@NonNull Class< T> modelClass)创建一个ViewModel对象,mFactory由创建ViewModelProvider指定,一般通过反射创建该对象。例如默认的DefaultFactory对象。
(4) 创建好ViewModel对象后将该对象缓存起来,保存到mViewModelStore中并返回ViewModel对象。

这样,整个ViewModel创建的过程就结束了。还是借用一下别人的序列图。
这里写图片描述

2 ViewModel实例为什么不会因为Activity 重新创建而销毁?
这个问题很有意思,从一开始接触到ViewModel官方的说明我就很好奇,这个是如何实现的呢?我们知道,在Activity/Fragment 因为屏幕旋转,配置改变时,里面所引用的对象都会被重建,典型的是对话框,为此我们不得不在Activity的onSaveInstanceState()保存一些数据,然后在onCreate()中恢复数据。然而ViewModel却不需要,那么ViewModel是如何做到的呢?我们查看ViewModelProviders的源码发现每次的of()方法又都是创建一个新的ViewModelProvider对象的,这就更加疑惑了。

    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        initializeFactoryIfNeeded(checkApplication(activity));
        return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory);
    }

答案是有两个关键点:
(1) HolderFragment在Activity/Fragment 被 re-create 的生命周期
(2) ViewModelProvider 中 mViewModelStore对象
这里分别依次来说明这两个关键点

(1) HolderFragment在Activity/Fragment 被 re-create 的生命周期
仔细阅读HolderFragment的源码,我们发现在构造方法中有这么一句

    public HolderFragment() {
        setRetainInstance(true);
    }

很不起眼,却非常重要。
setRetainInstance(true);表明Fragment 不会因为Activity/Fragment 因为配置改变,屏幕旋转被销毁时,他会驻留在系统中,知道Activity/Fragment 被重新创建,然后onAttach上继续使用,也就是说HolderFragment在Activity/Fragment 因为配置改变,屏幕旋转被销毁时,不会走onDestory() –> onCreate()方法。这样保证了Activity/Fragment重新创建时还是之前那个HolderFragment对象,当然了里面的mViewModelStore也不会变。

(2) ViewModelProvider 中 mViewModelStore对象
由前面的分析得到Activity/Fragment重新创建时HolderFragment不会被重新创建,那么自然ViewModelStores中返回的ViewModelStore还是之前的ViewModelStore

    @MainThread
    public static ViewModelStore of(@NonNull FragmentActivity activity) {
        return holderFragmentFor(activity).getViewModelStore();
    }

这样子导致在创建ViewModelProvider传进去的ViewModelStore 对象还是Activity/Fragment之前的ViewModelStore 对象

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

这样即使new 再多的ViewModelProvider实例,它里面的mViewModelStore 对象并没有被重新创建。这样在调用get(@NonNull String key, @NonNull Class < T> modelClass)方法时,将会在步骤(2)中返回,返回的ViewModel对象就是Activity/Fragment重新创建之前的那个ViewModel对象。

通过(1)(2)就保证了ViewModel实例不会因为Activity 重新创建而销毁。

3 HoldFragment解析
在ViewModel组件中定义了一个HoldFragment类,该类继承Fragment,但是里面没有任何布局文件,它最主要的作用就是来生成在Activity/Fragment重新创建时保证返回的ViewModelStore对象不会改变。为此它的实现如下:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
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 onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mViewModelStore.clear();
    }

    public ViewModelStore getViewModelStore() {
        return mViewModelStore;
    }

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

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

    @SuppressWarnings("WeakerAccess")
    static class HolderFragmentManager {
        private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
        private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>();

        private ActivityLifecycleCallbacks mActivityCallbacks =
                new EmptyActivityLifecycleCallbacks() {
                    @Override
                    public void onActivityDestroyed(Activity activity) {
                        HolderFragment fragment = mNotCommittedActivityHolders.remove(activity);
                        if (fragment != null) {
                            Log.e(LOG_TAG, "Failed to save a ViewModel for " + activity);
                        }
                    }
                };

        private boolean mActivityCallbacksIsAdded = false;

        private FragmentLifecycleCallbacks mParentDestroyedCallback =
                new FragmentLifecycleCallbacks() {
                    @Override
                    public void onFragmentDestroyed(FragmentManager fm, Fragment parentFragment) {
                        super.onFragmentDestroyed(fm, parentFragment);
                        HolderFragment fragment = mNotCommittedFragmentHolders.remove(
                                parentFragment);
                        if (fragment != null) {
                            Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment);
                        }
                    }
                };

        void holderFragmentCreated(Fragment holderFragment) {
            Fragment parentFragment = holderFragment.getParentFragment();
            if (parentFragment != null) {
                mNotCommittedFragmentHolders.remove(parentFragment);
                parentFragment.getFragmentManager().unregisterFragmentLifecycleCallbacks(
                        mParentDestroyedCallback);
            } else {
                mNotCommittedActivityHolders.remove(holderFragment.getActivity());
            }
        }

        private static HolderFragment findHolderFragment(FragmentManager manager) {
            if (manager.isDestroyed()) {
                throw new IllegalStateException("Can't access ViewModels from onDestroy");
            }

            Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
            if (fragmentByTag != null && !(fragmentByTag instanceof HolderFragment)) {
                throw new IllegalStateException("Unexpected "
                        + "fragment instance was returned by HOLDER_TAG");
            }
            return (HolderFragment) fragmentByTag;
        }

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

        HolderFragment holderFragmentFor(Fragment parentFragment) {
            FragmentManager fm = parentFragment.getChildFragmentManager();
            HolderFragment holder = findHolderFragment(fm);
            if (holder != null) {
                return holder;
            }
            holder = mNotCommittedFragmentHolders.get(parentFragment);
            if (holder != null) {
                return holder;
            }

            parentFragment.getFragmentManager()
                    .registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
            holder = createHolderFragment(fm);
            mNotCommittedFragmentHolders.put(parentFragment, holder);
            return holder;
        }
    }
}

首先在构造方法中调用了setRetainInstance(true),这样子在Activity/Fragment 因为配置发生改变时重新创建时,就不会销毁该HolderFragment 的实例,这样子HolderFragment 在下次Activity/Fragment重新创建时,并不会回调onDestory(),onCreate()方法,关于这一点。可以打点确认。
HoldFragment将创建实例的方法交给了HolderFragmentManager这个内部类,这个内部类首先创建了两个Map来保存了还未Attach但将要Attach 到Activity/Fragment的HoldFragment实例,并且注册了Activity/Fragment 的生命周期回调。接着创建HolderFragment实例,然后添加到map中去。

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

最后在HolderFragment的onCreate() 移除Map中添加的HoldFragment实例。因为回调到onCreate()方法,就说明HoldFragment已经成功的Attach到相应的Activity/Fragment 上了,这个时候需要移除mNotCommittedActivityHolders中的HoldFragment实例。

最后,在Activity finish之后,HoldFragment 会调用onDestory()方法,在这里面会清理掉mViewModelStore 中的ViewModel。

至此,ViewModel组件的分析已经基本完毕,下一篇将分析Android架构组件中LiveData组件

参考:
https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html
https://shymanzhu.com/2017/12/28/Android%E6%9E%B6%E6%9E%84%E7%BB%84%E4%BB%B6%EF%BC%88%E4%B8%89%EF%BC%89%E2%80%94%E2%80%94ViewModel/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值