之前工作用了很久MVP架構(gòu)了葱椭,雖然很好的解決了M層與V層的耦合關(guān)系吱涉,但巨多的接口莲组,難以復(fù)用罗标、難以單測(cè)的問(wèn)題一直縈繞心頭丈挟,久久不能平復(fù)~~~显沈,于是我將目光轉(zhuǎn)向了MVVM软瞎。
MVVM與MVP相比最大的區(qū)別就是用ViewModel(后文簡(jiǎn)稱VM)代替了原來(lái)的P層,這里的VM就是ViewModel拉讯。一句話概括它的特點(diǎn)---對(duì)數(shù)據(jù)狀態(tài)的持有和維護(hù)涤浇。換言之,它將原來(lái)P層關(guān)于數(shù)據(jù)的邏輯運(yùn)算與處理統(tǒng)一放到了VM中魔慷,而剩余的V層的操作建議使用Databinding只锭,從而形成最為簡(jiǎn)潔高效的MVVM架構(gòu)。說(shuō)到這呢院尔,推薦一篇舊文DataBinding蜻展,再學(xué)不會(huì)你砍我(系兄弟就砍偶系列?)。
回到VM的特點(diǎn)---對(duì)數(shù)據(jù)狀態(tài)的持有和維護(hù)邀摆。為什么需要做這些呢纵顾?事實(shí)上就是為了解決下面兩個(gè)開(kāi)發(fā)中常見(jiàn)的問(wèn)題。
- Activity配置更改重建時(shí)(比如屏幕旋轉(zhuǎn))保留數(shù)據(jù)
- UI組件(Activity與Fragment栋盹、Fragment與Fragment)間實(shí)現(xiàn)數(shù)據(jù)共享施逾。
對(duì)于第一條不用VM的情況下只能通過(guò)onSaveInstanceState
保存數(shù)據(jù),當(dāng)activity重建后再通過(guò)onCreate
或onRestoreInstanceState
方法的bundle中取出例获,但如果數(shù)據(jù)量較大汉额,數(shù)據(jù)的序列化和反序列化將產(chǎn)生一定的性能開(kāi)銷。
對(duì)于第二條如果不用VM榨汤,各個(gè)UI組件都要持有共享數(shù)據(jù)的引用闷愤,這會(huì)帶來(lái)兩個(gè)麻煩,第一件余,如果新增了共享數(shù)據(jù),各個(gè)UI組件需要再次聲明并初始化新增的共享數(shù)據(jù)遭居;第二啼器,某個(gè)UI組件對(duì)共享數(shù)據(jù)修改,無(wú)法直接通知其他UI組件俱萍,需手動(dòng)實(shí)現(xiàn)觀察者模式端壳。而VM結(jié)合LiveData就可以很輕松的實(shí)現(xiàn)這一點(diǎn)。
LiveData作為數(shù)據(jù)變化的驅(qū)動(dòng)器枪蘑,VM借助它可以寫(xiě)出十分簡(jiǎn)潔的MVVM代碼损谦。
接下來(lái)我們來(lái)看一下VM到底是如何實(shí)現(xiàn)上述需求的岖免,而事實(shí)上核心可以轉(zhuǎn)化為下面兩個(gè)問(wèn)題。
問(wèn)題
- VM是如何解決Activity與Fragment照捡、Fragment之間數(shù)據(jù)共享的問(wèn)題颅湘?
- VM是如何在Activity發(fā)生旋轉(zhuǎn)時(shí)保留數(shù)據(jù)的?不是也走onDestroy了嗎栗精?
ViewModel是什么闯参?
回答上面問(wèn)題之前我們要先了解一下VM到底是個(gè)啥。
public abstract class ViewModel {
protected void onCleared() {
}
}
就是個(gè)抽象類甚至連抽象方法都沒(méi)有悲立,簡(jiǎn)單的令人發(fā)指鹿寨。onCleared方法提供了釋放VM中資源的一個(gè)機(jī)會(huì),在Activity/Fragment的onDestroy生命周期中被調(diào)用薪夕。類庫(kù)內(nèi)部提供了一個(gè)實(shí)現(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的引用,所以通吃祝可以做一些全生命周期的工作馏慨。為什么不能持有一個(gè)Activity呢?這與ViewModel的生命周期有關(guān)嚼贡,我們稍后解釋熏纯。
創(chuàng)建ViewModel
接下來(lái)看看VM的創(chuàng)建,通常我們是這樣創(chuàng)建一個(gè)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);
}
這兩個(gè)方法的返回值都是ViewModelProvider
毒费,ViewModelProviders是操作ViewModelProvider的工具類,內(nèi)部都是獲取ViewModelProvider的靜態(tài)方法愈魏,而真正的VM的業(yè)務(wù)在ViewModelProvider中觅玻。
同理ViewModelStores和ViewModelStore的關(guān)系亦是如此。既然要保留VM的數(shù)據(jù)培漏,必然要有個(gè)存儲(chǔ)單元溪厘。ViewModelStore就是這個(gè)存儲(chǔ)單元,因?yàn)橐粋€(gè)Activity中可能有多個(gè)VM牌柄,所以需要一個(gè)Map來(lái)維護(hù)關(guān)系表畸悬,key為VM的名字,value為VM對(duì)象珊佣,簡(jiǎn)單的令人發(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又是什么鬼?它是抽象了一個(gè)獲取ViewModelStore的接口冷冗。常見(jiàn)的實(shí)現(xiàn)類有Fragment/FragmentActivity守屉,這也不難理解,因?yàn)橐朐谂渲酶淖儠r(shí)保留數(shù)據(jù)贾惦,首先得要將數(shù)據(jù)存儲(chǔ)起來(lái)胸梆。
public interface ViewModelStoreOwner {
ViewModelStore getViewModelStore();
}
感覺(jué)這時(shí)需要一張類圖了,不然你是不是想砍死我须板?
事實(shí)上目前提到的這幾個(gè)類已經(jīng)幾乎涵蓋了viewmodel庫(kù)中所有的類碰镜。
我們回到1.1中創(chuàng)建ViewModelProvider的代碼。
return new ViewModelProvider(ViewModelStores.of(activity), factory);
我們將activity中的VM存儲(chǔ)單元和VM的創(chuàng)建工廠(AndroidViewModelFactory)傳入得到一個(gè)ViewModelProvider對(duì)象习瑰,最終通過(guò)其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對(duì)象甜奄。
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)建一個(gè)新的VM并存入ViewModelStore
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
默認(rèn)情況下mFactory為AndroidViewModelFactory课兄,來(lái)看下它是如何創(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 {
//通過(guò)反射創(chuàng)建AndroidViewModel對(duì)象
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
...
}
}
return super.create(modelClass);
}
}
至此VM的創(chuàng)建我們就講完了,可以總結(jié)一下:
- ViewModelStore是存儲(chǔ)VM的數(shù)據(jù)單元烟阐,存儲(chǔ)結(jié)構(gòu)為Map搬俊,F(xiàn)ragment/FragmentActivity持有其引用。
- ViewModelProvider通過(guò)get方法創(chuàng)建一個(gè)VM蜒茄,創(chuàng)建之前會(huì)先檢查ViewModelStore中是否存在唉擂,若不存在則通過(guò)反射創(chuàng)建一個(gè)VM。
UI組件間數(shù)據(jù)共享
講到這我們回過(guò)頭來(lái)看看開(kāi)篇提的第一個(gè)問(wèn)題:Activity與Fragment檀葛,F(xiàn)ragment之間是如何共享數(shù)據(jù)的玩祟。
數(shù)據(jù)要實(shí)現(xiàn)共享最簡(jiǎn)單的方式就是大家都讀取一份數(shù)據(jù)源。
我們來(lái)看看數(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類型通過(guò)activity的getViewModelStore方法創(chuàng)建润讥。
②否則通過(guò)一個(gè)HolderFragment來(lái)創(chuàng)建转锈。
先來(lái)看①
### FragmentActivity
private ViewModelStore mViewModelStore;
public ViewModelStore getViewModelStore() {
...
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
return mViewModelStore;
}
簡(jiǎn)單的令人發(fā)ck,既然是成員變量那每次調(diào)用必定返回同一個(gè)VM象对,只要保證調(diào)用ViewModelProviders.of(activity).get(AndroidViewModel::class.java)
傳遞的activity是同一個(gè)對(duì)象。
再來(lái)看②宴抚,整體思路是添加一個(gè)不可見(jiàn)的HolderFragment到當(dāng)前Activity中勒魔,再將數(shù)據(jù)源記錄在這個(gè)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)前有沒(méi)有這個(gè)HolderFragment
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
return holder;
}
...
//沒(méi)有就創(chuàng)建一個(gè)
holder = createHolderFragment(fm);
...
return holder;
}
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
//創(chuàng)建一個(gè)HolderFragment、打上tag冠绢,再添加到當(dāng)前界面抚吠。
HolderFragment holder = new HolderFragment();
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}
之所以不可見(jiàn),是因?yàn)檫@個(gè)HolderFragment并未復(fù)寫(xiě)onCreateView弟胀。這么做的原因是早期的版本想利用Fragment的setRetainInstance()
API接口楷力,來(lái)實(shí)現(xiàn)當(dāng)Activity因配置發(fā)生改變時(shí)保留這個(gè)不可見(jiàn)的Fragment,生命周期只走onDetach和onAttach孵户。既然沒(méi)有被重建萧朝,那么它持有的數(shù)據(jù)自然就會(huì)被保留,其他主動(dòng)退出的情況走到onDestroy會(huì)清空數(shù)據(jù)夏哭。
### HolderFragment
public HolderFragment() {
setRetainInstance(true);
}
//被銷毀時(shí)清除數(shù)據(jù)源
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
那么在實(shí)際使用中會(huì)走哪條路徑呢检柬?我們反觀源碼FragmentActivity是實(shí)現(xiàn)了ViewModelStoreOwner接口的,那此處代碼不是肯定走①嗎竖配?②是不是走錯(cuò)片場(chǎng)了何址?此時(shí)我感覺(jué)我的智商受到了侮辱。
原來(lái)在appcompat-v7版本在27.1.0
之前FragmentActivity并沒(méi)有實(shí)現(xiàn)ViewModelStoreOwner接口进胯,也就是統(tǒng)一用HolderFragment實(shí)現(xiàn)用爪。google大大可能是覺(jué)得這個(gè)HolderFragment有點(diǎn)太騷操作了就讓后續(xù)版本的FragmentActivity直接支持了VM存儲(chǔ)。因?yàn)樘砑拥腍olderFragment也是有維護(hù)成本的胁镐,且上層可以通過(guò)FragmentManager獲取到它偎血,然后對(duì)它進(jìn)行其他騷操作,想想都刺激希停。烁巫。。
上面我們分析了創(chuàng)建VM時(shí)傳入FragmentActivity的情況宠能,那傳入Fragment的情況呢亚隙?實(shí)際上跟FragmentActivity幾乎一樣,在Fragment中也有一個(gè)mViewModelStore成員變量违崇,注意這里哦阿弃,非常關(guān)鍵。也就是說(shuō)如果我們通過(guò)下面的代碼創(chuàng)建的是兩個(gè)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)
}
正確的寫(xiě)法應(yīng)該是二者在of方法中傳入相同的對(duì)象
### TestFragment.onCreate(savedInstanceState: Bundle?)
//通過(guò)getActivity方法取得Fragment所在的Activity
val vm = activity?.run {
//run函數(shù) 這里的this指代activity
ViewModelProviders.of(this)[AndroidViewModel::class.java]
}
Fragment之間同理,如果Fragment間同層級(jí)伴箩,可以統(tǒng)一通過(guò)Activity或共同的parentFragment(如果有)獲取VM入愧;如果有嵌套關(guān)系,可以使用parentFragment對(duì)象獲取VM。
Activity配置發(fā)生變化時(shí)數(shù)據(jù)保持
其實(shí)在上面的內(nèi)容中已經(jīng)講了HolderFragment是如何保留數(shù)據(jù)的棺蛛,就是利用Fragment的setRetainInstance()
API接口完成的怔蚌。
google為了優(yōu)化這點(diǎn)在新版本里直接讓FragmentActivity支持了數(shù)據(jù)的保持,官方的配圖很好的闡釋了VM在Activity因配置變化銷毀重建時(shí)的生命周期旁赊。
下面我們來(lái)分析一下是如何做到的桦踊,先看onDestroy的銷毀邏輯。
### FragmentActivity
protected void onDestroy() {
super.onDestroy();
...
if (mViewModelStore != null && !mRetaining) {
mViewModelStore.clear();
}
...
}
mRetaining為true時(shí)將會(huì)保留VM數(shù)據(jù)终畅,那它何時(shí)為true呢籍胯?
### FragmentActivity
public final Object onRetainNonConfigurationInstance() {
if (mStopped) {
//方法內(nèi)部會(huì)將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ù)返回會(huì)被記錄
}
onRetainNonConfigurationInstance方法被retainNonConfigurationInstances方法調(diào)用,而它會(huì)被ActivityThread中performDestroyActivity方法調(diào)用离福,它執(zhí)行在onDestroy生命周期之前杖狼。
### ActivityThread
performDestroyActivity(...boolean getNonConfigInstance,...) {
...
if (getNonConfigInstance) {
//如果配置發(fā)生改變記錄下來(lái)
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
}
}
這樣VM數(shù)據(jù)就被封裝到了NonConfigurationInstances一個(gè)對(duì)象中了。
那何時(shí)被還原呢术徊?答案是Activity的attach時(shí)本刽。
### Activity
final void attach(Context context, ... NonConfigurationInstances lastNonConfigurationInstances,...) {
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
}
VM數(shù)據(jù)源mViewModelStore在onCreate時(shí)被重新賦值。
### 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)致重建時(shí)會(huì)被保留子寓,從生命周期的角度來(lái)說(shuō),ViewModel的生命周期可能會(huì)長(zhǎng)于Activity的生命周期笋除。
這說(shuō)明我們?cè)谑褂肰iewModel時(shí)一定要注意斜友,不能讓其引用Activity或View,否則可能導(dǎo)致內(nèi)存泄漏垃它。
好了鲜屏,整個(gè)ViewModel的分析就結(jié)束了,希望你在了解其工作原理的同時(shí)對(duì)MVVM模式也有一些新的認(rèn)識(shí)国拇。獨(dú)立的看ViewModel沒(méi)有什么實(shí)際意義洛史,更像是數(shù)據(jù)容器,結(jié)合LiveData使用更容易理解酱吝,后續(xù)章節(jié)會(huì)繼續(xù)分享LiveData的內(nèi)容也殖。