Android Architecture Component之ViewModel解析
之前給大家分析過了 LiveData ,今天就來看看 ViewModel 。
ViewModel 的作用就相當於 MVP 中的 Presenter ,是用來銜接 Model 和 View 的。通常把一些與 View 無關的業務邏輯寫在 ViewModel 裡面。ViewModel 內部創建出 LiveData 物件,利用 LiveData 物件來傳遞資料給 View 。
ViewModel 相對於 Presenter 而言,有以下幾個好處:
- ViewModel 並不直接持有 View ,所以在 ViewModel 銷燬時不需要像 Presenter 一樣地去手動解除 View 的繫結,也就不會造成持有 View 導致的記憶體洩漏;
- 比如 Activity 配置改變的情況下,ViewModel 會儲存不會丟失資料;
- ViewModel 可以做到在同一個 Activity 的情況下,多個 Fragment 共享資料;
下面是官方給出的 ViewModel 生命週期圖,大家隨意感受一下:
那麼就開始進入正題吧。
本次解析的 ViewModel 原始碼基於 android.arch.lifecycle:extensions:1.1.1
ViewModel
先來看看 ViewModel 是怎麼被創建出來的:
XXXViewModel xxxViewModel = ViewModelProviders.of(activity).get(XXXViewModel.class)
可以看到 ViewModel 並不是簡單地 new 出來的,這其中的邏輯要需要我們一步一步慢慢揭開。
那麼 ViewModel 是怎樣被定義的呢?
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() { } }
原來 ViewModel 是個抽象類,裡面只有一個 onCleared() 方法。 onCleared() 會在 ViewModel 被銷燬時回撥,所以可以在 onCleared() 裡面做一些釋放資源、清理記憶體的操作。
另外,ViewModel 還有一個子類: AndroidViewModel 。AndroidViewModel 在 ViewModel 的基礎上內部包含了 application 。
ViewModelProviders
我們就來抽絲剝繭了,先從 ViewModelProviders 入手。建立 ViewModel 時在 ViewModelProviders 中呼叫了 of 方法。
of
@NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity) { return of(activity, null); } @NonNull @MainThread public static ViewModelProvider of(@NonNull Fragment fragment) { return of(fragment, null); } @NonNull @MainThread 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); } @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); }
of 方法可以分為兩個入口,分別對應著 Fragment 和 Activity 。這也說明了 ViewModel 的作用域其實是分為兩個維度的。但是這兩個方法內部的程式碼很像,邏輯基本都是:
- 先去獲取 application ;
- 建立 factory ;
- 建立 ViewModelProvider ,ViewModelProvider 顧名思義就是提供 ViewModel 的;
第一步就不用說了,直接進入第二步吧。
Factory
Factory 是什麼東東呢,說白了就是 ViewModel 的製造工廠。所有的 ViewModel 都是由 Factory 來創建出來的。
public interface Factory { /** * Creates a new instance of the given {@code Class}. * <p> * * @param modelClass a {@code Class} whose instance is requested * @param <T>The type parameter for the ViewModel. * @return a newly created ViewModel */ @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass); }
Factory 是個介面,裡面定義了 create 方法來建立 ViewModel 。來看看它的實現類 NewInstanceFactory 。
NewInstanceFactory
/** * Simple factory, which calls empty constructor on the give class. */ public static class NewInstanceFactory implements Factory { @SuppressWarnings("ClassNewInstance") @NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { //noinspection TryWithIdenticalCatches try { 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); } } }
其實沒啥好說的,就是利用反射來建立例項了,是一個很簡單的實現類。NewInstanceFactory 其實是建立普通 ViewModel 的工廠,而如果想建立 AndroidViewModel 的話,工廠就要選擇 AndroidViewModelFactory 了。
AndroidViewModelFactory
/** * {@link Factory} which may create {@link AndroidViewModel} and * {@link ViewModel}, which have an empty constructor. */ public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory { private static AndroidViewModelFactory sInstance; /** * Retrieve a singleton instance of AndroidViewModelFactory. * * @param application an application to pass in {@link AndroidViewModel} * @return A valid {@link AndroidViewModelFactory} */ @NonNull public static AndroidViewModelFactory getInstance(@NonNull Application application) { if (sInstance == null) { sInstance = new AndroidViewModelFactory(application); } return sInstance; } private Application mApplication; /** * Creates a {@code AndroidViewModelFactory} * * @param application an application to pass in {@link AndroidViewModel} */ 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); } }
發現在 AndroidViewModelFactory 的 create 方法中,對建立 ViewModel 的方案做了相容,所以 AndroidViewModelFactory 是同時適用於建立 ViewModel 和 AndroidViewModel 的。並且 AndroidViewModelFactory 是單例工廠,防止多次建立浪費記憶體。
額外補充一點,在 ViewModelProviders 中有一個內部類 DefaultFactory ,現在已經被打上廢棄的標籤了,可以猜出這個 DefaultFactory 應該是早期版本的預設工廠類,現在已經被 AndroidViewModelFactory 代替了。
ViewModelStores
到這裡 Factory 就有了,那麼就重點來看看 ViewModelStores.of(activity)
這段程式碼了。ViewModelStores 是根據作用域用來提供 ViewModelStore 的,而 ViewModelStore 的作用就是儲存 ViewModel ,內部是利用 key/value 將 ViewModel 儲存在 HashMap 中,方便讀寫,這裡就不展示 ViewModelStore 的原始碼了,大家可以把 ViewModelStore 當作 HashMap 就行。
/** * Factory methods for {@link ViewModelStore} class. */ @SuppressWarnings("WeakerAccess") public class ViewModelStores { private ViewModelStores() { } /** * 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(); } /** * Returns the {@link ViewModelStore} of the given fragment. * * @param fragment a fragment whose {@code ViewModelStore} is requested * @return a {@code ViewModelStore} */ @NonNull @MainThread public static ViewModelStore of(@NonNull Fragment fragment) { if (fragment instanceof ViewModelStoreOwner) { return ((ViewModelStoreOwner) fragment).getViewModelStore(); } return holderFragmentFor(fragment).getViewModelStore(); } }
根據 ViewModelProviders 的思路,ViewModelStores 也是分為了兩個方法,對應著 Fragment 和 Activity 。
- 如果 Activity 和 Fragment 實現了 ViewModelStoreOwner 的介面,那麼直接返回內部的 ViewModelStore 就行了;
- 如果是之前老早版本的 Activity 或者 Fragment ,那麼它們肯定是沒有實現 ViewModelStoreOwner 介面的,那該怎麼辦呢?很簡單,新建立一個 Fragment 來關聯 ViewModelStoreOwner 就好了啊!
所以就有了 holderFragmentFor(activity) 和 holderFragmentFor(fragment) 這段了。
HolderFragment
HolderFragment 實現了 ViewModelStoreOwner 介面,所以 HolderFragment 的作用就是代替了那些之前沒有實現 ViewModelStoreOwner 介面的 Activity/Fragment 。這樣,Activity/Fragment 也間接地擁有了 ViewModelStore 。
HolderFragment 的程式碼我們就只看 holderFragmentFor(activity) 這一段吧,holderFragmentFor(fragment) 也是類似的。
/** * @hide */ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public static HolderFragment holderFragmentFor(FragmentActivity activity) { return sHolderFragmentManager.holderFragmentFor(activity); } static class HolderFragmentManager { ... 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 新增進 Activity 裡面,這樣 HolderFragment 就和 Activity 的生命週期關聯在一起了。實際上獲取的就是 HolderFragment 裡面的 ViewModelStore 。每個 Activity 裡面只有一個 HolderFragment 。
Fragment 也是同理,利用 getChildFragmentManager() 來往裡新增 HolderFragment 。這裡就不講了,有興趣的同學可以自己回去看看原始碼。
至此,用來建立 ViewModelProvider 的兩個入參 ViewModelStore 和 Factory 都講完了。
ViewModelProvider
創建出 ViewModelProvider 後,最後一步就是呼叫它的 get 方法返回 ViewModel 了。
@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. } } viewModel = mFactory.create(modelClass); mViewModelStore.put(key, viewModel); //noinspection unchecked return (T) viewModel; }
get 方法很 easy ,就是利用 class 的 canonicalName 生成一個唯一的 key ,然後利用 key 去 mViewModelStore 中獲取。如果有值就返回,否則就利用 factory 建立新的 ViewModel ,然後儲存到 mViewModelStore 中並返回。
整個 ViewModel 的原始碼流程基本上就講完了,其實並不複雜。回去多多體會,總能明白其中的奧祕。
下面,額外給大家補充幾個小點,加個雞腿。
Tip
ViewModel的onCleared什麼時候回撥
之前說過,ViewModel 是儲存在 ViewModelStore 裡面的,所以 ViewModel 的銷燬一定是在 ViewModelStore 裡面操作的。
ViewModelStore
/** *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 的 clear() 方法內部呼叫 ViewModel 的 onCleared() 方法。那麼哪裡呼叫了 ViewModelStore 的 clear() 方法呢?
Fragment
/** * Called when the fragment is no longer in use.This is called * after {@link #onStop()} and before {@link #onDetach()}. */ @CallSuper public void onDestroy() { mCalled = true; // Use mStateSaved instead of isStateSaved() since we're past onStop() if (mViewModelStore != null && !mHost.mFragmentManager.mStateSaved) { mViewModelStore.clear(); } }
可以從程式碼上看到,Fragment 的銷燬操作呼叫是在 onDestroy() 中。
另外,如果狀態儲存標記值 mStateSaved 為 true 的情況下,是不會去清除 ViewModel 的,這也是為什麼上面中講的配置改變的情況下,資料得以保持住的原因。
FragmentActivity
/** * Destroy all fragments. */ @Override protected void onDestroy() { super.onDestroy(); doReallyStop(false); if (mViewModelStore != null && !mRetaining) { mViewModelStore.clear(); } mFragments.dispatchDestroy(); }
同理, Activity 的銷燬操作也是在 onDestroy() 完成的。