之前工作用了很久MVP架構(gòu)了,雖然很好的解決了M層與V層的耦合關(guān)系,但巨多的接口,難以復(fù)用、難以單測的問題一直縈繞心頭,久久不能平復(fù)~~~,于是我將目光轉(zhuǎn)向了MVVM。
MVVM與MVP相比最大的區(qū)別就是用ViewModel(后文簡稱VM)代替了原來的P層,這里的VM就是ViewModel。一句話概括它的特點---對數(shù)據(jù)狀態(tài)的持有和維護(hù)。換言之,它將原來P層關(guān)于數(shù)據(jù)的邏輯運算與處理統(tǒng)一放到了VM中,而剩余的V層的操作建議使用Databinding,從而形成最為簡潔高效的MVVM架構(gòu)。說到這呢,推薦一篇舊文DataBinding,再學(xué)不會你砍我(系兄弟就砍偶系列?)。
回到VM的特點---對數(shù)據(jù)狀態(tài)的持有和維護(hù)。為什么需要做這些呢?事實上就是為了解決下面兩個開發(fā)中常見的問題。
- Activity配置更改重建時(比如屏幕旋轉(zhuǎn))保留數(shù)據(jù)
- UI組件(Activity與Fragment、Fragment與Fragment)間實現(xiàn)數(shù)據(jù)共享。
對于第一條不用VM的情況下只能通過onSaveInstanceState保存數(shù)據(jù),當(dāng)activity重建后再通過onCreate或onRestoreInstanceState方法的bundle中取出,但如果數(shù)據(jù)量較大,數(shù)據(jù)的序列化和反序列化將產(chǎn)生一定的性能開銷。
對于第二條如果不用VM,各個UI組件都要持有共享數(shù)據(jù)的引用,這會帶來兩個麻煩,第一,如果新增了共享數(shù)據(jù),各個UI組件需要再次聲明并初始化新增的共享數(shù)據(jù);第二,某個UI組件對共享數(shù)據(jù)修改,無法直接通知其他UI組件,需手動實現(xiàn)觀察者模式。而VM結(jié)合LiveData就可以很輕松的實現(xiàn)這一點。
LiveData作為數(shù)據(jù)變化的驅(qū)動器,VM借助它可以寫出十分簡潔的MVVM代碼。
接下來我們來看一下VM到底是如何實現(xiàn)上述需求的,而事實上核心可以轉(zhuǎn)化為下面兩個問題。

問題
- VM是如何解決Activity與Fragment、Fragment之間數(shù)據(jù)共享的問題?
- VM是如何在Activity發(fā)生旋轉(zhuǎn)時保留數(shù)據(jù)的?不是也走onDestroy了嗎?
ViewModel是什么?
回答上面問題之前我們要先了解一下VM到底是個啥。
public abstract class ViewModel {
protected void onCleared() {
}
}
就是個抽象類甚至連抽象方法都沒有,簡單的令人發(fā)指。onCleared方法提供了釋放VM中資源的一個機(jī)會,在Activity/Fragment的onDestroy生命周期中被調(diào)用。類庫內(nèi)部提供了一個實現(xiàn)類AndroidViewModel。
public class AndroidViewModel extends ViewModel {
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
public <T extends Application> T getApplication() {
return (T) mApplication;
}
}
AndroidViewModel內(nèi)持有Application的引用,所以通??梢宰鲆恍┤芷诘墓ぷ鳌槭裁床荒艹钟幸粋€Activity呢?這與ViewModel的生命周期有關(guān),我們稍后解釋。
創(chuàng)建ViewModel
接下來看看VM的創(chuàng)建,通常我們是這樣創(chuàng)建一個VM的。
val viewModel = ViewModelProviders.of(this).get(AndroidViewModel::class.java)
這里的this可以是FragmentActivity或Fragment,他們都是support-v4包中控件,為的是向下兼容。
我們以FragmentActivity為例看看of方法做了什么。
### 1.1 ViewModelProviders
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) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
//這里用的工廠模式,vm的創(chuàng)建交由工廠完成,默認(rèn)使用AndroidViewModelFactory
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
這兩個方法的返回值都是ViewModelProvider,ViewModelProviders是操作ViewModelProvider的工具類,內(nèi)部都是獲取ViewModelProvider的靜態(tài)方法,而真正的VM的業(yè)務(wù)在ViewModelProvider中。
同理ViewModelStores和ViewModelStore的關(guān)系亦是如此。既然要保留VM的數(shù)據(jù),必然要有個存儲單元。ViewModelStore就是這個存儲單元,因為一個Activity中可能有多個VM,所以需要一個Map來維護(hù)關(guān)系表,key為VM的名字,value為VM對象,簡單的令人發(fā)指。
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
ViewModelStores僅負(fù)責(zé)提供工具方法創(chuàng)建ViewModelStore。
### 1.2
public class ViewModelStores {
private ViewModelStores() {
}
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
return holderFragmentFor(activity).getViewModelStore();
}
...
}
ViewModelStoreOwner又是什么鬼?它是抽象了一個獲取ViewModelStore的接口。常見的實現(xiàn)類有Fragment/FragmentActivity,這也不難理解,因為要想在配置改變時保留數(shù)據(jù),首先得要將數(shù)據(jù)存儲起來。
public interface ViewModelStoreOwner {
ViewModelStore getViewModelStore();
}
感覺這時需要一張類圖了,不然你是不是想砍死我?

事實上目前提到的這幾個類已經(jīng)幾乎涵蓋了viewmodel庫中所有的類。
我們回到1.1中創(chuàng)建ViewModelProvider的代碼。
return new ViewModelProvider(ViewModelStores.of(activity), factory);
我們將activity中的VM存儲單元和VM的創(chuàng)建工廠(AndroidViewModelFactory)傳入得到一個ViewModelProvider對象,最終通過其get方法得到VM。
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);
}
以Default_Key作為前綴加上VM的完整類名為key,獲取VM對象。
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
//先看ViewModelStore中是否存在,如果存了就直接返回
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
}
...
//如果不存在,創(chuàng)建一個新的VM并存入ViewModelStore
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
默認(rèn)情況下mFactory為AndroidViewModelFactory,來看下它是如何創(chuàng)建VM的。
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
...
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
try {
//通過反射創(chuàng)建AndroidViewModel對象
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
...
}
}
return super.create(modelClass);
}
}
至此VM的創(chuàng)建我們就講完了,可以總結(jié)一下:
- ViewModelStore是存儲VM的數(shù)據(jù)單元,存儲結(jié)構(gòu)為Map,F(xiàn)ragment/FragmentActivity持有其引用。
- ViewModelProvider通過get方法創(chuàng)建一個VM,創(chuàng)建之前會先檢查ViewModelStore中是否存在,若不存在則通過反射創(chuàng)建一個VM。
UI組件間數(shù)據(jù)共享
講到這我們回過頭來看看開篇提的第一個問題:Activity與Fragment,F(xiàn)ragment之間是如何共享數(shù)據(jù)的。
數(shù)據(jù)要實現(xiàn)共享最簡單的方式就是大家都讀取一份數(shù)據(jù)源。
我們來看看數(shù)據(jù)源ViewModelStore具體是在哪里創(chuàng)建的。
上面講的代碼片段1.2可知,有兩條路徑創(chuàng)建。
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) { ①
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
return holderFragmentFor(activity).getViewModelStore();②
}
①如果FragmentActivity是ViewModelStoreOwner類型通過activity的getViewModelStore方法創(chuàng)建。
②否則通過一個HolderFragment來創(chuàng)建。
先來看①
### FragmentActivity
private ViewModelStore mViewModelStore;
public ViewModelStore getViewModelStore() {
...
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
return mViewModelStore;
}
簡單的令人發(fā)ck,既然是成員變量那每次調(diào)用必定返回同一個VM,只要保證調(diào)用ViewModelProviders.of(activity).get(AndroidViewModel::class.java)傳遞的activity是同一個對象。
再來看②,整體思路是添加一個不可見的HolderFragment到當(dāng)前Activity中,再將數(shù)據(jù)源記錄在這個fragment中。
### HolderFragment extends Fragment implements ViewModelStoreOwner
private ViewModelStore mViewModelStore = new ViewModelStore();
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
return sHolderFragmentManager.holderFragmentFor(activity);
}
HolderFragment holderFragmentFor(FragmentActivity activity) {
FragmentManager fm = activity.getSupportFragmentManager();
//先查一下當(dāng)前有沒有這個HolderFragment
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
return holder;
}
...
//沒有就創(chuàng)建一個
holder = createHolderFragment(fm);
...
return holder;
}
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
//創(chuàng)建一個HolderFragment、打上tag,再添加到當(dāng)前界面。
HolderFragment holder = new HolderFragment();
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}
之所以不可見,是因為這個HolderFragment并未復(fù)寫onCreateView。這么做的原因是早期的版本想利用Fragment的setRetainInstance()API接口,來實現(xiàn)當(dāng)Activity因配置發(fā)生改變時保留這個不可見的Fragment,生命周期只走onDetach和onAttach。既然沒有被重建,那么它持有的數(shù)據(jù)自然就會被保留,其他主動退出的情況走到onDestroy會清空數(shù)據(jù)。
### HolderFragment
public HolderFragment() {
setRetainInstance(true);
}
//被銷毀時清除數(shù)據(jù)源
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
那么在實際使用中會走哪條路徑呢?我們反觀源碼FragmentActivity是實現(xiàn)了ViewModelStoreOwner接口的,那此處代碼不是肯定走①嗎?②是不是走錯片場了?此時我感覺我的智商受到了侮辱。

原來在appcompat-v7版本在27.1.0之前FragmentActivity并沒有實現(xiàn)ViewModelStoreOwner接口,也就是統(tǒng)一用HolderFragment實現(xiàn)。google大大可能是覺得這個HolderFragment有點太騷操作了就讓后續(xù)版本的FragmentActivity直接支持了VM存儲。因為添加的HolderFragment也是有維護(hù)成本的,且上層可以通過FragmentManager獲取到它,然后對它進(jìn)行其他騷操作,想想都刺激。。。

上面我們分析了創(chuàng)建VM時傳入FragmentActivity的情況,那傳入Fragment的情況呢?實際上跟FragmentActivity幾乎一樣,在Fragment中也有一個mViewModelStore成員變量,注意這里哦,非常關(guān)鍵。也就是說如果我們通過下面的代碼創(chuàng)建的是兩個VM,是不能共享數(shù)據(jù)的。
### MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
//傳遞activity
val vm = ViewModelProviders.of(this).get(AndroidViewModel::class.java)
}
### TestFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//傳遞fragment
val vm = ViewModelProviders.of(this).get(AndroidViewModel::class.java)
}
正確的寫法應(yīng)該是二者在of方法中傳入相同的對象
### TestFragment.onCreate(savedInstanceState: Bundle?)
//通過getActivity方法取得Fragment所在的Activity
val vm = activity?.run {
//run函數(shù) 這里的this指代activity
ViewModelProviders.of(this)[AndroidViewModel::class.java]
}
Fragment之間同理,如果Fragment間同層級,可以統(tǒng)一通過Activity或共同的parentFragment(如果有)獲取VM;如果有嵌套關(guān)系,可以使用parentFragment對象獲取VM。
Activity配置發(fā)生變化時數(shù)據(jù)保持
其實在上面的內(nèi)容中已經(jīng)講了HolderFragment是如何保留數(shù)據(jù)的,就是利用Fragment的setRetainInstance()API接口完成的。
google為了優(yōu)化這點在新版本里直接讓FragmentActivity支持了數(shù)據(jù)的保持,官方的配圖很好的闡釋了VM在Activity因配置變化銷毀重建時的生命周期。

下面我們來分析一下是如何做到的,先看onDestroy的銷毀邏輯。
### FragmentActivity
protected void onDestroy() {
super.onDestroy();
...
if (mViewModelStore != null && !mRetaining) {
mViewModelStore.clear();
}
...
}
mRetaining為true時將會保留VM數(shù)據(jù),那它何時為true呢?
### FragmentActivity
public final Object onRetainNonConfigurationInstance() {
if (mStopped) {
//方法內(nèi)部會將mRetaining設(shè)置為true
doReallyStop(true);
}
...
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
//保存VM數(shù)據(jù)
nci.viewModelStore = mViewModelStore;
nci.fragments = fragments;
return nci;//數(shù)據(jù)返回會被記錄
}
onRetainNonConfigurationInstance方法被retainNonConfigurationInstances方法調(diào)用,而它會被ActivityThread中performDestroyActivity方法調(diào)用,它執(zhí)行在onDestroy生命周期之前。
### ActivityThread
performDestroyActivity(...boolean getNonConfigInstance,...) {
...
if (getNonConfigInstance) {
//如果配置發(fā)生改變記錄下來
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
}
}
這樣VM數(shù)據(jù)就被封裝到了NonConfigurationInstances一個對象中了。
那何時被還原呢?答案是Activity的attach時。
### Activity
final void attach(Context context, ... NonConfigurationInstances lastNonConfigurationInstances,...) {
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
}
VM數(shù)據(jù)源mViewModelStore在onCreate時被重新賦值。
### FragmentActivity
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//還原數(shù)據(jù)
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
...
}
最后我們整理一下整體流程圖。

從上面的流程可以看出VM在Activity因配置變化導(dǎo)致重建時會被保留,從生命周期的角度來說,ViewModel的生命周期可能會長于Activity的生命周期。
這說明我們在使用ViewModel時一定要注意,不能讓其引用Activity或View,否則可能導(dǎo)致內(nèi)存泄漏。
好了,整個ViewModel的分析就結(jié)束了,希望你在了解其工作原理的同時對MVVM模式也有一些新的認(rèn)識。獨立的看ViewModel沒有什么實際意義,更像是數(shù)據(jù)容器,結(jié)合LiveData使用更容易理解,后續(xù)章節(jié)會繼續(xù)分享LiveData的內(nèi)容。