Android架構(gòu)組件之ViewModel源碼解析

ViewModel是Google官方MVVM架構(gòu)的核心組件之一。同時,在Google推出的Android Jetpack組件中,也將ViewModel放在了Architecture類別之中。ViewModel類旨在以一種有生命周期的方式存儲和管理與UI相關(guān)的數(shù)據(jù),并且不會因?yàn)槠聊恍D(zhuǎn)等配置變化后而銷毀。


ViewModel生命周期

從ViewModel生命周期圖中可以看出我們發(fā)現(xiàn),當(dāng)activity因屏幕旋轉(zhuǎn)而銷毀,但是ViewModel一直存在,正是因?yàn)閂iewModel有如此的生命周期,所以ViewModel在MVVM中可以作為數(shù)據(jù)存儲區(qū),是連接View和Model重要組件。
本文主要通過分析ViewModel源碼,搞清楚如下幾個問題:

  • ViewModel是如何創(chuàng)建的?
  • ViewModel是如何存儲的,具體放在哪?
  • ViewModel如何實(shí)現(xiàn)旋轉(zhuǎn)屏幕而不銷毀?

一、ViewModel創(chuàng)建過程

使用過ViewModel的同學(xué)都應(yīng)該知道,當(dāng)我們想要創(chuàng)建一個ViewModel對象的時候,不是通過new關(guān)鍵字去創(chuàng)建的,而是通過ViewModelProviders.of(this).get(ViewModel.class)這種調(diào)用方式創(chuàng)建的,簡言之就是通過ViewModelProviders去創(chuàng)建。其實(shí),我們想想也可以知道,ViewModel想要有生命周期,那么它勢必就要和我們的Activity或者Fragment相關(guān)聯(lián)起來,至于如何關(guān)聯(lián),源碼會給我們答案。

我們先來看下ViewModelProviders.of()方法,調(diào)用這個方法最終會走到下面這2個方法其中之一,一個是在Activity里面調(diào)用,另一個則是在Fragment中調(diào)用:

    public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
        Application application = checkApplication(checkActivity(fragment));
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(ViewModelStores.of(fragment), factory);
    }

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

我們看到這個方法最終會返回一個ViewModelProvider對象,在生成這個對象的時候需要傳遞2個參數(shù),第一個參數(shù)是ViewModelStore,第二個參數(shù)是Factory。第一參數(shù)我們等到討論ViewModel具體是存儲在哪邊的時候再說,暫且略過。這邊先重點(diǎn)看下第二個參數(shù)Factory,從代碼上來看,如何我們沒有顯示地傳入Factory對象的話,就會去拿一個默認(rèn)的Factory對象。事實(shí)也就是如此,而且這個默認(rèn)的Factory對象還是個單例。其實(shí),AndroidViewModelFactory就是ViewModelProvider的一個靜態(tài)內(nèi)部類,具體代碼如下所示:

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

至此,我們的ViewModelProvider對象總算是創(chuàng)建完成了,接下來就要調(diào)用它的get()方法來具體創(chuàng)建我們所需要的ViewModel對象了,具體如下:

    // ViewModelProvider # get(@NonNull Class<T> modelClass)
    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);
    }

    // ViewModelProvider # get(@NonNull String key, @NonNull Class<T> modelClass)
    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;
    }

由上述代碼我們可以知道,對于我們想要獲取的ViewModel對象,首先會根據(jù)key從mViewModelStore中去尋找,看看是否已經(jīng)創(chuàng)建過,如果找到則直接返回。如果之前沒有創(chuàng)建過,則調(diào)用mFactory的create()方法去創(chuàng)建,如果我們沒有顯示地傳入Factory對象,則調(diào)用默認(rèn)的Factory對象(即AndroidViewModelFactory單例對象)去創(chuàng)建,由AndroidViewModelFactory源碼可知它的create()方法會通過反射去創(chuàng)建我們的ViewModel對象。在創(chuàng)建完畢之后,就會把這個ViewModel對象存入mViewModelStore之中,那么很明顯這個mViewModelStore內(nèi)部肯定是一個HashMap結(jié)構(gòu)。至此,我們已經(jīng)將ViewModel創(chuàng)建過程解析完畢了,接下來看下創(chuàng)建好的ViewModel是存儲在什么地方的。

二、ViewModel存儲位置

在解析ViewModel創(chuàng)建過程時,我們遺漏了一個點(diǎn),那就是創(chuàng)建ViewModelProvider對象時傳入的第一個參數(shù)ViewModelStore,它是通過ViewModelStores.of(activity)或者ViewModelStores.of(fragment)來創(chuàng)建的。其實(shí),創(chuàng)建出來的ViewModelStore對象最后復(fù)制給了我們上面所提到的mViewModelStore,具體可以看下如下源碼:

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

而我們通過分析ViewModel創(chuàng)建過程,知道了最終創(chuàng)建出來的ViewModel對象,就是存儲在mViewModelStore之中的。那么,現(xiàn)在就很明顯了,ViewModel的存儲位置其實(shí)就是mViewModelStore的存儲位置,我們需要搞清楚ViewModelStores.of()方法的執(zhí)行過程就行。

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

    public static ViewModelStore of(@NonNull Fragment fragment) {
        if (fragment instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) fragment).getViewModelStore();
        }
        return holderFragmentFor(fragment).getViewModelStore();
    }

我們具體看下ViewModelStores.of()方法的執(zhí)行過程,由于我們的Activity或者Fragment一般不會去實(shí)現(xiàn)ViewModelStoreOwner接口,所以基本都是通過holderFragmentFor(xxx).getViewModelStore()這種方式去創(chuàng)建ViewModelStore對象。
那么,我們先來看下這個創(chuàng)建方式的前半段holderFragmentFor(xxx):

    public static HolderFragment holderFragmentFor(FragmentActivity activity) {
        return sHolderFragmentManager.holderFragmentFor(activity);
    }

    public static HolderFragment holderFragmentFor(Fragment fragment) {
        return sHolderFragmentManager.holderFragmentFor(fragment);
    }

很明顯,這個方法返回的是一個HolderFragment對象,而且它繼承自Fragment。HolderFragment對象的創(chuàng)建都是通過調(diào)用sHolderFragmentManager.holderFragmentFor()方法實(shí)現(xiàn)的,那么這個sHolderFragmentManager其實(shí)是HolderFragment內(nèi)部類HolderFragmentManager的對象,我們一起看下它的holderFragmentFor()方法,這里以入?yún)锳ctivity為例:

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

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

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

由源碼可知,會先去查找Tag為HOLDER_TAG的HolderFragment是否已經(jīng)創(chuàng)建過,如果已經(jīng)創(chuàng)建過則直接返回,從這我們可以得到的信息是一個Activity/Fragment只會創(chuàng)建一個HolderFragment。如果沒有找到,則會從mNotCommittedActivityHolders緩存中進(jìn)行查找,同樣如果找到就直接返回。如果仍然沒有找到,則說明之前未創(chuàng)建過,則調(diào)用createHolderFragment()方法創(chuàng)建HolderFragment對象,并在完成創(chuàng)建之后存入mNotCommittedActivityHolders。同時,我們還注意到,這里還注冊了Activity生命周期監(jiān)聽,目的是在Activity銷毀的時候可以移除mNotCommittedActivityHolders中的對應(yīng)數(shù)據(jù)。
之后,在拿到HolderFragment對象之后,通過調(diào)用它的getViewModelStore()方法就可以拿到ViewModelStore對象了,它其實(shí)就是HolderFragment的成員變量。

    public ViewModelStore getViewModelStore() {
        return mViewModelStore;
    }

至此,通過以上分析,我們知道通過ViewModelStores.of()方法獲取的ViewModelStore對象其實(shí)就是HolderFragment的成員變量。那么,也就是說我們創(chuàng)建的ViewModel對象其實(shí)也就是保存在所創(chuàng)建出來無UI的HolderFragment之中的。需要注意的一點(diǎn)是,對于同一個Activity/Fragment而言,這種無UI的HolderFragment只會存在一個,但是存在HolderFragment之中的ViewModel可以有多個,即一個Activity/Fragment可以對應(yīng)多個ViewModel對象。

三、ViewModel旋轉(zhuǎn)屏幕不銷毀原理

ViewModel旋轉(zhuǎn)屏幕不銷毀的原理其實(shí)就在HolderFragment的構(gòu)造方法之中。

    public HolderFragment() {
        setRetainInstance(true);
    }

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

    /**
     * Control whether a fragment instance is retained across Activity
     * re-creation (such as from a configuration change).  This can only
     * be used with fragments not in the back stack.  If set, the fragment
     * lifecycle will be slightly different when an activity is recreated:
     * <ul>
     * <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
     * will be, because the fragment is being detached from its current activity).
     * <li> {@link #onCreate(Bundle)} will not be called since the fragment
     * is not being re-created.
     * <li> {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} <b>will</b>
     * still be called.
     * </ul>
     */
    public void setRetainInstance(boolean retain) {
        mRetainInstance = retain;
    }

由源碼可知,setRetainInstance(boolean) 是Fragment中的一個方法。根據(jù)官方注釋,我們可以知道,當(dāng)設(shè)置setRetainInstance(true)時,當(dāng)我們旋轉(zhuǎn)屏幕的時候,Activity會重繪,但是Fragment不會重繪,它將會保留,這也就意味著旋轉(zhuǎn)屏幕時Fragment的onCreate和onDestory方法都不會調(diào)用。
無UI的HolderFragment正是運(yùn)用了這個特性,在其中存放一個專門用于存儲ViewModel對象的ViewModelStore(其內(nèi)部為HashMap),如此ViewModelStore中所有的ViewModel都不會因?yàn)槠聊恍D(zhuǎn)等配置變化后而銷毀。

總結(jié)

在創(chuàng)建ViewModel對象的過程中,會生成一個無UI的HolderFragment。對于同一個Activity/Fragment,只會有一個HolderFragment,但是可以有多個ViewModel。ViewModel存儲在無UI的HolderFragment中,具體以鍵值對形式存儲在ViewModelStore的HashMap中。Activity/Fragment的HolderFragment會保存在全局單例的HolderFragmentManager的HashMap中,在Activity/Fragment銷毀時會進(jìn)行相應(yīng)的移除。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容