Android架構元件(Architecture Components)之 ViewModel詳解
寫在前面
元件架構三劍客終於來到了最後一篇:ViewModel,關於Lifecycle和LiveData可以看之前的文章。ViewModel和Lifecycle和LiveData的關聯並不大,可以單獨拿出來使用。這裡用的依賴主要是AndroidX裡面的,其他版本可能有些不同,但核心邏輯應該還是一致的。
implementation 'androidx.appcompat:appcompat:1.1.0-alpha01' implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0-alpha01'
我們都知道,ViewModel的核心功能之一就是幫我們儲存資料,特別是當Activity重建時還能將資料儲存下來。頁面重建儲存資料,看到這裡我們可能很容易的就想到了 onSaveInstanceState()
方法,我們可以在這裡儲存資料,以便Activity重新建立時在 onCreate()
方法中接收到儲存下來的資料。但它是有侷限的,只能儲存一些簡單的資料或者一些經過序列化的資料,並且資料量還不能太大。ViewModel的出現,正好彌補了這一不足。
ViewModel結構與Lifecycle的結構類似,Activity/Fragment除了實現一個LifecycleOwner介面外,還實現了一個ViewModelStoreOwner介面,它只有一個方法用來獲取ViewModelStore,然後通過ViewModelStore來負責新增和移除ViewModel。

viewmodel.png
ViewModelProvider
ViewModel的建立是ViewModelProvider提供的。要看ViewModel是怎麼被建立並新增進ViewModelStore的,還得先從我們最熟悉的api入手。
// 這裡的this是指Activity viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
先建立一個ViewModelProvider,再通過它來獲取ViewModel。
@NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity) { return of(activity, null); } @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(activity.getViewModelStore(), factory); } public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { this(store, new FactoryWrapper(factory)); } public ViewModelProvider(@NonNull ViewModelStore store, @NonNull KeyedFactory factory) { // 要注意這裡的mFactory是KeyedFactory mFactory = factory; mViewModelStore = store; }
Factory
這個方法的目的是要構造一個ViewModelProvider,先來講講工廠Factory,ViewModelStore
放到後面講。預設我們沒有傳工廠進來,這裡會幫我們構建一個AndroidViewModelFactory物件。因為這裡面出現了很多Factory相關的類,所以我覺得還是有必要先將Factory的結構講一下,有助於瞭解。

factory.png
結合上面的程式碼和類圖來看,這個框架會預設給我們提供一個AndroidViewModelFactory工廠物件,然後又將它封裝成了一個KeyedFactory物件,再加上ViewModelStore物件,一起構造出了ViewModelProvider。短短的一個方法裡面出現了工廠模式(Factory)和裝飾器模式(FactoryWrapper),真的是很佩服。
ViewModel
拿到ViewModelProvider物件後,再來看它的get方法:
@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); } @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. } } // mFactory指的是FactoryWrapper viewModel = mFactory.create(key, modelClass); mViewModelStore.put(key, viewModel); //noinspection unchecked return (T) viewModel; }
首先是根據class獲取一個key值,然後先從mViewModelStore中查詢有沒有相應的ViewModel,當然我們第一次呼叫肯定是拿不到的,需要走下面的建立步驟,建立完再新增到ViewModelStore中去。
private static class FactoryWrapper implements KeyedFactory { private final Factory mFactory; FactoryWrapper(Factory factory) { mFactory = factory; } @Override public <T extends ViewModel> T create(String key, Class<T> modelClass) { // mFactory指的是AndroidViewModelFactory return mFactory.create(modelClass); } }
很奇怪的是這裡的key並沒有被用到,不知道後面會不會新增一些新的東西進來。
@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 (...) { // 一些catch } } return super.create(modelClass); }
如果這個ViewModel是繼承自AndroidViewModel的話,就用AndroidViewModelFactory建立一個,否則,用NewInstanceFactory建立一個普通的ViewModel。
Android給的建議是不要在ViewModel中使用到任何與Android相關的程式碼,最簡單的檢查辦法就是看有沒有import進 *.android.*
相關的東西。但是這又有點不現實,因為我們經常需要用到Context去獲取一些資源。為了解決這個問題,Android就給我們提供了AndroidViewModel。所以如果有這個需求的話可以讓你的ViewModel繼承AndroidViewModel。
另外,從ViewModel的建立過程來看,如果我們需要在建構函式裡傳一些別的引數的話,就需要自己去構建工廠類了。
講完了Factory,再回過頭來看ViewModelStore,Activity/Fragment建立ViewModel的過程唯一的區別就在於ViewModelStore獲取方式的不同。
activity.getViewModelStore(); fragment.getViewModelStore();
Activity.getViewModelStore()
@NonNull @Override public ViewModelStore getViewModelStore() { ...... if (mViewModelStore == null) { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { // Restore the ViewModelStore from NonConfigurationInstances mViewModelStore = nc.viewModelStore; } if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } return mViewModelStore; }
首次呼叫這個方法,肯定是通過new方法拿到的,可以猜出ViewModelStore的快取就是通過NonConfigurationInstances快取下來的。而它又是在 onRetainNonConfigurationInstance()
方法中儲存下來的,然後使用 getLastNonConfigurationInstance()
獲取出來的。這個與 onSaveInstanceState()
和 onRestoreInstanceState()
是類似的。
@Override @Nullable public final Object onRetainNonConfigurationInstance() { Object custom = onRetainCustomNonConfigurationInstance(); ViewModelStore viewModelStore = mViewModelStore; if (viewModelStore == null) { // No one called getViewModelStore(), so see if there was an existing // ViewModelStore from our last NonConfigurationInstance NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { viewModelStore = nc.viewModelStore; } } if (viewModelStore == null && custom == null) { return null; } NonConfigurationInstances nci = new NonConfigurationInstances(); nci.custom = custom; nci.viewModelStore = viewModelStore; return nci; }
關於 onRetainNonConfigurationInstance()
和 getLastNonConfigurationInstance()
這兩個方法可以來Activity中看一下具體的流程。首先狀態得儲存下來,才能在重建的時候取出來。既然是要儲存狀態,那肯定是在 onDestroy()
的時候儲存的,所以直接來看看 ActivityThread$performDestroyActivity()
// ActivityThread.java ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) { ActivityClientRecord r = mActivities.get(token); Class<? extends Activity> activityClass = null; if (localLOGV) Slog.v(TAG, "Performing finish of " + r); if (r != null) { activityClass = r.activity.getClass(); r.activity.mConfigChangeFlags |= configChanges; if (finishing) { r.activity.mFinished = true; } // 如果還沒呼叫onPause()的話,呼叫onPause() performPauseActivityIfNeeded(r, "destroy"); if (!r.stopped) { // 呼叫onStop() callActivityOnStop(r, false /* saveState */, "destroy"); } if (getNonConfigInstance) { try { // 劃重點,儲存狀態 r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances(); } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to retain activity " + r.intent.getComponent().toShortString() + ": " + e.toString(), e); } } } try { r.activity.mCalled = false; // 呼叫onDestroy() mInstrumentation.callActivityOnDestroy(r.activity); if (!r.activity.mCalled) { throw new SuperNotCalledException( "Activity " + safeToComponentShortString(r.intent) + " did not call through to super.onDestroy()"); } if (r.window != null) { r.window.closeAllPanels(); } } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to destroy activity " + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } r.setState(ON_DESTROY); } mActivities.remove(token); StrictMode.decrementExpectedActivityCount(activityClass); return r; }
再繼續跟到Activity裡面
NonConfigurationInstances retainNonConfigurationInstances() { Object activity = onRetainNonConfigurationInstance(); HashMap<String, Object> children = onRetainNonConfigurationChildInstances(); FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig(); mFragments.doLoaderStart(); mFragments.doLoaderStop(true); ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig(); if (activity == null && children == null && fragments == null && loaders == null && mVoiceInteractor == null) { return null; } NonConfigurationInstances nci = new NonConfigurationInstances(); nci.activity = activity; nci.children = children; nci.fragments = fragments; nci.loaders = loaders; if (mVoiceInteractor != null) { mVoiceInteractor.retainInstance(); nci.voiceInteractor = mVoiceInteractor; } return nci; }
第一行就是呼叫 onRetainNonConfigurationInstance()
來儲存Activity的狀態。這個方法返回的NonConfigurationInstances物件儲存在ActivityClientRecord中,然後在重啟Activity的 onAttach()
方法中將NonConfigurationInstances拿回來,從而實現了資料的不丟失。
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window, ActivityConfigCallback activityConfigCallback) { ...... // 取回NonConfigurationInstances mLastNonConfigurationInstances = lastNonConfigurationInstances; }
Fragment.getViewModelStore()
// Fragment.java @NonNull @Override public ViewModelStore getViewModelStore() { if (mFragmentManager == null) { throw new IllegalStateException("Can't access ViewModels from detached fragment"); } return mFragmentManager.getViewModelStore(this); } // FragmentManagerImpl.java @NonNull ViewModelStore getViewModelStore(@NonNull Fragment f) { return mNonConfig.getViewModelStore(f); } // FragmentManagerViewModel.java @NonNull ViewModelStore getViewModelStore(@NonNull Fragment f) { ViewModelStore viewModelStore = mViewModelStores.get(f.mWho); if (viewModelStore == null) { viewModelStore = new ViewModelStore(); mViewModelStores.put(f.mWho, viewModelStore); } return viewModelStore; }
可以看到,最後是通過FragmentManagerViewModel來儲存ViewModelStore的,key是相應的Fragment的ID。哦對了,FragmentManagerViewModel自己也是一個ViewModel。
Fragment的資料恢復是在 FragmentActivity$onCreate()
裡面做的。
@SuppressWarnings("deprecation") @Override protected void onCreate(@Nullable Bundle savedInstanceState) { // 在這裡恢復資料 mFragments.attachHost(null /*parent*/); super.onCreate(savedInstanceState); ...... }
最終走到FragmentManagerImpl中去:
public void attachController(@NonNull FragmentHostCallback host, @NonNull FragmentContainer container, @Nullable Fragment parent) { if (mHost != null) throw new IllegalStateException("Already attached"); mHost = host; mContainer = container; mParent = parent; if (parent != null) { mNonConfig = parent.mFragmentManager.getChildNonConfig(parent); } else if (host instanceof ViewModelStoreOwner) { // 這裡是通過Activity的getViewModelStore()獲取ViewModelStore的 ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore(); mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore); } else { mNonConfig = new FragmentManagerViewModel(false); } }
在這裡初始化 mNonConfig
,所以我們才能通過 mNonConfig.getViewModelStore()
去獲取ViewModelStore。重新建立Activity的時候,是通過Activity的 getViewModelStore()
去拿到ViewModelStore的,再通過它去拿到FragmentManagerViewModel。這裡感覺有點繞。可以這麼說吧,Fragment的ViewModelStore是存放到FragmentManagerViewModel中的,然後FragmentManagerViewModel又被放到了Activity的ViewModelStore中,Activity在儲存資料的時候自然也就將FragmentManagerViewModel儲存了下來,從而將Fragment的ViewModelStore儲存了下來。
另外,網上有一些文章說Fragment的ViewModelStore是通過 Fragment$setRetainInstance()
來儲存的,可能是引用的版本號不一樣吧,畢竟Android還在繼續更新中。我這裡通過除錯發現是以這種方式儲存的。可能以後版本不一樣又會有所改動了。
最後
到這裡,Lifecycle, LiveData和ViewModel的原理,總算是解析完了。其中至少是用到了觀察者模式,工廠模式,裝飾器模式,自己分析完之後還是有所收穫的。也可以看到ViewModel的原理和Activity的生命週期最加緊密,先給自己挖個坑,後面找時間再來寫一篇Activity的啟動流程的。