Android 官方架構元件(三)——ViewModel

刺目啊1199發表於2018-12-28

初到掘金,人生地不熟,喜歡的朋友,點個贊鼓勵下新手唄~

參考文章:
https://developer.android.google.cn/topic/libraries/architecture/viewmodel
https://mp.weixin.qq.com/s/thoXHuXHC3sV90IFttHfXw
https://blog.csdn.net/gaugamela/article/details/56280384

ViewModel類主要用來儲存和管理與UI相關的資料,它能夠讓資料在螢幕旋轉等配置資訊改變導致UI重建的情況下不被銷燬。

ViewModel生命週期

ViewModel物件存活在系統中不被回收的時間是由建立ViewModel傳遞給ViewModelProviderLifecycle決定的。ViewModel將一直留在記憶體中,直到限定其存在時間範圍的Lifecycle生命週期結束。對於Activity,是在Activity destroy時;而對於Fragment,是在Fragment detach時。

下圖說明了Activity經歷螢幕旋轉直到最終destroyed掉這整個過程中所經歷的各個生命週期。該圖還在關聯的Activity生命週期的旁邊顯示了ViewModel的生命週期。

我們可以看出,ViewModel的生命週期貫穿Activity始終,直到Activity正常結束,並不會因為螢幕旋轉等系統原因而導致ViewModel生命週期提前結束。同理Fragment對於ViewModel也一樣。

               說明 ViewModel 隨著 Activity 狀態的改變而經歷的生命週期。

本文,我們不討論ViewModel的使用,而是直接分析它的原理。

ViewModel原理分析

我們分析原始碼的時候要帶著明確目的帶著問題去分析,從小見大,一個一個小問題解決,無頭蒼蠅似的撲進原始碼中,可能事倍功半。

在分析ViewModel原始碼之前,我們先介紹一下ViewModel元件中重要的幾個類:

  • ViewModel、ViewModelProvider、HolderFragment、ViewModelStore

Android 官方架構元件(三)——ViewModel

上面的類圖簡單地描述出了這幾個類的作用以及關係,算是一個小概括,下面,我們直接進入原始碼分析它們具體的實現,以及它們是如何聯絡起來的。

(在這裡預設大家已經知道了ViewModel的使用)我們知道ViewModel例項的建立是通過 ViewModelProviders.of(this).get(XxxViewModel.class)生成的,這行程式碼可以分為兩個步驟:

  • 第一步:通過ViewModelProviders.of(this)生成ViewModelProvider物件;
  • 第二步:通過viewModelProvider.get(XxxViewModel.class)生成相對ViewModel物件。

下面我們的原始碼就針對這兩個步驟分析一下ViewModel架構中重要的幾個類。

ViewModelProvider

ViewModelProvider是通過ViewModelProviders.of(this)生成的,我們進入原始碼看看:

public static ViewModelProvider of(@NonNull FragmentActivity activity) {
    return of(activity, null);
}

public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
        // 上面的類圖,我們已經說了,生成 ViewModelProvider 例項的時候,如果 factory 傳 null,
        // 系統會預設使用 AndroidViewModelFactory 作為 ViewModel 的生產類
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }

    // 例項化建立一個 ViewModelProvider 物件返回
    return new ViewModelProvider(ViewModelStores.of(activity), factory);
}複製程式碼

可以看到,ViewModelProviders.of()最終會new一個ViewModelProvider物件返回。在建立ViewModelProvider的時候,需要傳入兩個引數,ViewModelStore以及Factory。在這裡,我們先不討論ViewModelStores.of(activity)是如何例項化返回ViewModelStore物件的,在後面,我們會說到。

先來看一下Factory類,它的定義很簡單:

public interface Factory {
    // 只有一個 create 方法需要重寫,這個方法就返回 ViewModel 物件,
    // 我們可以選擇預設使用 AndroidViewModelFactory,也可以自定義 Factory
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}複製程式碼

當我們傳入的factory引數為null的時候,會預設使用AndroidViewModelFactory

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

        // 當 modelClass 類繼承自 AndroidViewModel
        if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
            //noinspection TryWithIdenticalCatches
            try {
                // 呼叫 modelClass 類中帶有 Application 引數的構造方法建立一個 ViewMode 返回
                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);
            }
        }

        // 如果 modelClass 類不是繼承自 AndroidViewModel,呼叫 AndroidViewModelFactory 的
        // 父類 NewInstanceFactory 的 create 方法生成 ViewModel 物件返回
        return super.create(modelClass);
    }
}複製程式碼

AndroidViewModelFactory的父類NewInstanceFactory也是非常簡單:

public static class NewInstanceFactory implements Factory {

    @SuppressWarnings("ClassNewInstance")
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        //noinspection TryWithIdenticalCatches
        try {
            // 直接通過反射呼叫 modelClass 類的無參建構函式例項化一個 ViewModel 物件返回
            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);
        }
    }
}複製程式碼

根據上面對Factory的分析,在傳入的factory引數為null的前提下,我們可以得出以下結論:

  • 如果我們自定義的model繼承自ViewModel,需要有一個預設無參的構造方法;
  • 如果我們自定義的model繼承自AndroidViewModel,必須要有一個以Application為唯一引數的建構函式。

當然我們也可以自定義Factory,在這裡就不討論了,廣大聰明的讀者才是最法力無邊的~

ViewModelStore

上面我們知道了在構造ViewModelProvider的時候,會通過ViewModelStores.of()方法獲取到一個ViewModelStore物件。ViewModelStores類是用於提供返回ViewModelStore物件的,下面我們來看看ViewModelStores.of()究竟做了什麼?

public class ViewModelStores {

    private ViewModelStores() {
    }

    @NonNull
    @MainThread
    public static ViewModelStore of(@NonNull FragmentActivity activity) {

        // 開發者可以選擇自己讓 Activity 繼承 ViewModelStoreOwner,然後實現 getViewModelStore 方法
        if (activity instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) activity).getViewModelStore();
        }
        
        // holderFragmentFor(activity)方法返回繼承了 ViewModelStoreOwner 的 HolderFragment 例項,
        // 呼叫這個 HolderFragment 物件的 getViewModelStore() 即可以拿到 ViewModelStore 例項。
        return holderFragmentFor(activity).getViewModelStore();
    }

    @NonNull
    @MainThread
    public static ViewModelStore of(@NonNull Fragment fragment) {
        // 同理,fragment 也可以繼承 ViewModelStoreOwner ,實現 getViewModelStore 方法
        if (fragment instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) fragment).getViewModelStore();
        }
        // 同上
        return holderFragmentFor(fragment).getViewModelStore();
    }
}複製程式碼

可以看出,ViewModelStores.of()方法主要就是從ViewModelStoreOwner物件中獲取ViewModelStore物件。至於holderFragmentFor()方法究竟做了什麼?我們下面在繼續分析,現在,先看看ViewModelStore

public class ViewModelStore {
    
    // 用 HashMap 儲存 ViewModel 物件
    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    // 新添 ViewModel
    final void put(String key, ViewModel viewModel) {
        // 如果對應 key 的 ViewModel 已經存在,那麼覆蓋它,並且呼叫它的 onCleared 方法
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }
    
    // 獲取指定 key 的 ViewModel
    final ViewModel get(String key) {
        return mMap.get(key);
    }
    
    // 呼叫所有 ViewModel 的 onCleared 方法,並且清空整個 HashMap
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}複製程式碼

ViewModelStore的實現很簡單,主要的作用就是對ViewModel進行儲存管理,通過一個HashMap儲存了所有的ViewModel,分別通過put方法以及get方法新增和獲取ViewModel,通過clear方法遍歷呼叫所有ViewModelonCleared方法並且清空map。

接下來,我們來看看最核心的一個類,HolderFragment

HolderFragment

前面我們分析ViewModelStores.of()方法的時候,提到過呼叫靜態方法holderFragmentFor()方法能夠返回一個繼承了ViewModelStoreOwnerHolderFragment例項,現在我們就來看看HolderFragment這個類究竟是何方神聖?為什麼當Activity由於螢幕旋轉等被系統銷燬時,這個Fragment例項也不會被銷燬?

public class HolderFragment extends Fragment implements ViewModelStoreOwner {
    private static final String LOG_TAG = "ViewModelStores";

    // 這是什麼?請看下面註釋分析
    private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();

    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static final String HOLDER_TAG = "android.arch.lifecycle.state.StateProviderHolderFragment";

    // 這就是我們存放 ViewModel 的 ViewModelStore,就定義在 HolderFragment裡
    private ViewModelStore mViewModelStore = new ViewModelStore();

    public HolderFragment() {
        // 劃重點啦!!!為什麼當 activity 由於螢幕旋轉等被系統銷燬時,
        // 這個 fragment 例項也不會被銷燬?因為設定了 setRetainInstance(true)
        setRetainInstance(true);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 當 Fragment 的 onCreate 方法執行,說明了 Fragment 已經成功新增到了 Activity,
        // sHolderFragmentManager 是 HolderFragmentManager類,它的 holderFragmentCreated()方法
        // 是將該 Fragment 從 mNotCommittedActivityHolders 或 mNotCommittedFragmentHolders 中移除
        // (HolderFragmentManager 的說明,請看下面的註釋)
        sHolderFragmentManager.holderFragmentCreated(this);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 當一個設定了 setRetainInstance(true) 的 Fragment 的 onDestroy 方法被呼叫,
        // 證明它依附的 Activity 已經壽終正寢,所以呼叫 mViewModelStore.clear(),
        // 前面我們已經說了,這個 clear 方法會呼叫所有 ViewModel 物件的 onCleared 方法
        // 並且清空它們,我們可以在 ViewModel 的onCleared 方法做一些處理,以免起來不必要的
        // 記憶體洩漏等問題
        mViewModelStore.clear();
    }

    // 該方法用於給外部呼叫,返回 ViewModelStore
    @Override
    public ViewModelStore getViewModelStore() {
        return mViewModelStore;
    }

    // 靜態方法,沒 ViewModelStores.of 方法中被呼叫
    // 作用:在 activity 中新增一個 HolderFragment 用於儲存存放了ViewModel物件的ViewModelStore
    public static HolderFragment holderFragmentFor(FragmentActivity activity) {
        return sHolderFragmentManager.holderFragmentFor(activity);
    }

    // 靜態方法,沒 ViewModelStores.of 方法中被呼叫
    // 作用:在 fragment 中新增一個 HolderFragment 用於儲存存放了ViewModel物件的ViewModelStore
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static HolderFragment holderFragmentFor(Fragment fragment) {
        return sHolderFragmentManager.holderFragmentFor(fragment);
    }

    // 上面的大部分操作都是基於HolderFragmentManager,我們來分析下這個類
    @SuppressWarnings("WeakerAccess")
    static class HolderFragmentManager {

        // 存放還沒被系統正式新增到 Activity 中的 HolderFragment
        private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
        private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>();

        // 宣告定義了一個能夠感知 Activity 生命週期的 ActivityLifecycleCallbacks 
        private ActivityLifecycleCallbacks mActivityCallbacks =
                new EmptyActivityLifecycleCallbacks() {
                    @Override
                    public void onActivityDestroyed(Activity activity) {
                        // 當 Activity destroy 的時候,清除 mNotCommittedActivityHolders 中儲存
                        // 的對應 HolderFragment。前面我們分析了 HolderFragment 的 onCreate 方法中
                        // 會請一次 mNotCommittedActivityHolders,為什麼在這麼還要多此一舉呢?其實
                        // 並不是多此一舉,因為 Fragment 有可能還沒建立完,Activity 就夭折了,那這樣子
                        // HodlerFragment 的 onCreate 就無法呼叫,所以在加多一層清理機制,確保能夠
                        // 清除掉(不得不感嘆,谷歌官方的嚴謹以及對原始碼的掌控理解能力)
                        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) {
                        // 與 mActivityCallbacks 的分析同理
                        super.onFragmentDestroyed(fm, parentFragment);
                        HolderFragment fragment = mNotCommittedFragmentHolders.remove(
                                parentFragment);
                        if (fragment != null) {
                            Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment);
                        }
                    }
                };

        // HolderFragment 的 onCreate 生命週期被回撥,就會呼叫這個方法,清除
        // mNotCommittedActivityHolders 或者 mNotCommittedFragmentHolders 中
        // 的引用的 HolderFragment
        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);
            // 我們新新增 add 的 Fragment 並不會馬上就執行新增完(也就是說,這個方法執行完成後,馬上再
            // 呼叫一次,上面的 findHolderFragment 會返回 null。但是這沒有關係,因為接下來我們還可
            // 從 mNotCommittedActivityHolders 獲取到對應的例項),所以我們這裡先把他放在
            // mNotCommittedActivityHolders 中。Not Committed 表示 fragment 的 commit 還沒有完成
            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;
        }
    }
}複製程式碼

通過上面的註釋,我們已經詳細地對HolderFragment這個核心類做了分析,總結一下:

  • HolderFragment通過設定setRetainInstance(true),使得自身能夠不受到螢幕旋轉等configuration changes影響而存活,直到依附的Activity正常結束。
  • 因為HolderFragment的生命週期,ViewModelStore物件儲存在HolderFragment中,而ViewModel又儲存在ViewModelStore中,這就是為什麼我們說ViewModel類能夠讓資料在螢幕旋轉等配置資訊改變導致UI重建的情況下不被銷燬。

總結

ViewModel的分析就到此為止了,我們主要解決了兩個問題:

  • ViewModel是怎樣建立的?

通過呼叫ViewModelProviders.of(this).get(XxxViewModel.class)或者ViewModelProviders.of(this,  mFactory).get(XxxViewModel.class)返回ViewModel。最終ViewModel實際上是由Factory建立,當我們不傳Factory引數時,系統預設使用AndroidViewModelFactory作為Factory,通過反射生成ViewModel例項物件返回。

  • ViewModel以及儲存在其中的資料是怎樣在螢幕旋轉下依然保留在記憶體中的?

GC垃圾回收機制不會回收被強引用的物件。在開發過程中,我們需要儲存的資料被ViewModel引用,ViewModelViewModelStore引用,而ViewModelStore又被HolderFragment引用,於是就形成了一條引用鏈:HolderFragment->ViewModelStore->ViewModel->我們想要儲存的資料(最佳實踐是LiveData)。通過上面HolderFragment的分析,我們知道HodlerFragment在建立時,設定了setRetainInstance(true),因此它使得自身能夠不受到螢幕旋轉等configuration changes影響而存活,直到依附的Activity正常結束。


最後,附上一副時序圖供諸位朋友們幫助理解記憶:

Android 官方架構元件(三)——ViewModel

初到掘金,人生地不熟,喜歡的朋友,點個贊鼓勵下新手唄~

相關文章