ViewModel
是google官方的MVVM架構(gòu)組件藐握,目前已經(jīng)集成到了最新的支持庫中了,是MVVM架構(gòu)的核心組件之一拉背。不懂MVVM的請看之前的文章:(一)Android官方MVVM框架實現(xiàn)組件化之整體結(jié)構(gòu)
網(wǎng)上看到的ViewModel的博文千篇一律,實在忍不了,自己寫看了源碼寫了一篇网棍,歡迎拍磚!
ViewModel是存儲UI相關(guān)數(shù)據(jù)并不會因為旋轉(zhuǎn)而銷毀的類妇智。
最為重要的就是ViewModel具有下面的生命周期滥玷,這就是ViewModel的最可貴之處:
正因為ViewModel有如此的生命周期巍棱,所以ViewModel在MVVM可以作為數(shù)據(jù)存儲區(qū)惑畴,是連接View和Model重要組件,ViewModel的核心作用如下圖所示:
這篇文字要弄清楚下面幾個問題:
- 1.ViewModel是怎么創(chuàng)建的航徙?
- 2.ViewModel是怎么存儲的如贷?
- 3.ViewModel為什么可以實現(xiàn)旋轉(zhuǎn)屏幕不銷毀?
先放簡單講一下ViewModel
的基本使用方法到踏,我們在獲取ViewModel
的時候絕對不能直接使用new
關(guān)鍵字去創(chuàng)建杠袱,需要使用 ViewModelProviders
去使用系統(tǒng)提供的反射方法去創(chuàng)建我們想要的ViewModel
,下面是官方架構(gòu)組件android.arch.lifecycle包下面的ViewModelProviders
工具類用來獲取ViewModel:
/**
* 注解by danxx on 2018/3/31.
* Global ViewModel Provider
* ViewModel的創(chuàng)建不可直接new窝稿,需要使用這個{@link ViewModelProviders}才能與Activity或者
* Fragment的生命周期關(guān)聯(lián)起來楣富!
*/
public class ViewModelProviders {
/**
* 通過Activity獲取可用的Application
* 或者檢測Activity是否可用
* @param activity
* @return
*/
private static Application checkApplication(Activity activity) {
Application application = activity.getApplication();
if (application == null) {
throw new IllegalStateException("Your activity/fragment is not yet attached to "
+ "Application. You can't request ViewModel before onCreate call.");
}
return application;
}
/**
* 通過Fragment獲取Activity
* 或者檢測Fragment是否可用
* @param fragment
* @return
*/
private static Activity checkActivity(Fragment fragment) {
Activity activity = fragment.getActivity();
if (activity == null) {
throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
}
return activity;
}
/**
* 通過Fragment獲得ViewModelProvider
* @param fragment
* @return
*/
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
/**獲取默認的單例AndroidViewModelFactory,它內(nèi)部是通過反射來創(chuàng)建具體的ViewModel*/
ViewModelProvider.AndroidViewModelFactory factory =
ViewModelProvider.AndroidViewModelFactory.getInstance(
checkApplication(checkActivity(fragment)));
/***
* 利用HolderFragment來關(guān)聯(lián)生命周期并使用HolderFragment中的ViewModelStore的HashMap存儲ViewModel
* AndroidViewModelFactory創(chuàng)建ViewModel
*/
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
/**
* 通過FragmentActivity獲得ViewModelProvider
* @param activity
* @return
*/
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
/**獲取默認的單例AndroidViewModelFactory伴榔,它內(nèi)部是通過反射來創(chuàng)建具體的ViewModel*/
ViewModelProvider.AndroidViewModelFactory factory =
ViewModelProvider.AndroidViewModelFactory.getInstance(
checkApplication(activity));
/***
* 利用HolderFragment來關(guān)聯(lián)生命周期并使用HolderFragment中的ViewModelStore的HashMap存儲ViewModel
* AndroidViewModelFactory創(chuàng)建ViewModel
*/
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
/**
*
* @param fragment
* @param factory 提供了自定義創(chuàng)建ViewModel的方法
* @return
*/
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull ViewModelProvider.Factory factory) {
//檢測Fragment
checkApplication(checkActivity(fragment));
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
/**
*
* @param activity
* @param factory 提供了自定義創(chuàng)建ViewModel的方法
* @return
*/
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@NonNull ViewModelProvider.Factory factory) {
//檢測activity
checkApplication(activity);
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
}
有一些解釋我已經(jīng)放在注釋里了纹蝴,有興趣的可以仔細看看。
開始創(chuàng)建使用ViewModel了:
1.在Activity中創(chuàng)建使用ViewModel:
/**轉(zhuǎn)入Activity就行*/
GirlsViewModel girlsViewModel =
ViewModelProviders.of(ActivityGirls.this).get(GirlsViewModel.class);
2.在Fragment中創(chuàng)建使用ViewModel:
/**轉(zhuǎn)入Fragment就行*/
GirlsViewModel girlsViewModel =
ViewModelProviders.of(FragmentGirls.this).get(GirlsViewModel.class);
3.在任意地方創(chuàng)建使用ViewModel:
/**將context強轉(zhuǎn)成FragmentActivity就行*/
GirlsViewModel girlsViewModel =
ViewModelProviders.of((FragmentActivity) context).get(GirlsViewModel.class);
ViewModel
的存在是依賴 Activity
或者 Fragment
的潮梯,不管你在什么地方獲取ViewModel
骗灶,只要你用的是相同的Activity
或者 Fragment
,那么獲取到的ViewModel
將是同一個 (前提是key
值是一樣的)秉馏,所以ViewModel
也具有數(shù)據(jù)共享的作用耙旦!
一、ViewModel是怎么創(chuàng)建的?
上面創(chuàng)建ViewModel
鏈式調(diào)用分解為下面兩步:
/*****第一步:根據(jù)Activity或者Fragment獲得ViewModelProvider****/
ViewModelProvider viewModelProvider = ViewModelProviders.of(ActivityGirls.this);
/*****第二步:使用ViewModelProvider反射創(chuàng)建需要的ViewModel****/
GirlsViewModel girlsViewModel = viewModelProvider.get(GirlsViewModel.class);
先看第一步獲得的源代碼:
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
/**********獲得AndroidViewModelFactory ( 內(nèi)部是單例的 )*******/
ViewModelProvider.AndroidViewModelFactory factory =
ViewModelProvider.AndroidViewModelFactory.getInstance(
checkApplication(activity));
/*****創(chuàng)建一個ViewModelProvider( 傳入的兩個參數(shù)是重點 )*****/
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
上面的兩步其實很關(guān)鍵了免都,獲得AndroidViewModelFactory 锉罐,AndroidViewModelFactory
其實是ViewModelProvider
的靜態(tài)內(nèi)部類,看調(diào)用方式就知道是一個單例的绕娘,就是我們的應用只有有一個單例的 AndroidViewModelFactory
存在脓规,看源碼:
/*****`AndroidViewModelFactory `其實是`ViewModelProvider`的靜態(tài)內(nèi)部類******/
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
/********獲得AndroidViewModelFactory 單例**********/
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
/*************其實構(gòu)造方式是public 的,還是可以new的****************/
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
/******其實這里就是創(chuàng)建ViewModel的關(guān)鍵地方险领,根據(jù)給出的Class反射創(chuàng)建需要的ViewModel*******/
@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
工具類挨下,作用就是反射創(chuàng)建我們想要的類ViewModel
,其實功能簡單的脐湾!
獲得到的單例AndroidViewModelFactory
是創(chuàng)建ViewModelProvider
的第二個參數(shù)臭笆,下面我們看第一個參數(shù)。
第一個參數(shù)是這樣: ViewModelStores.of(activity)
看源碼:
解釋我就放在注釋了秤掌,大家看下面的注釋把:
@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
//如果你的Activity實現(xiàn)了ViewModelStoreOwner接口具備了提供
//ViewModelStore 的功能就直接獲取返回愁铺,通常我們的Activity都不會去實現(xiàn)這個功能
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
//系統(tǒng)為你的Activity添加一個具有提供ViewModelStore 的holderFragment
return holderFragmentFor(activity).getViewModelStore();
}
其實解析ViewModelStore
就可以解釋ViewModel的存儲,解析 holderFragmentFor(activity).getViewModelStore()
就可解釋ViewModel為什么可以在Activity配置發(fā)生變化的情況下人不銷毀闻鉴,這些我們就在下面去解釋茵乱。我第一步重點解釋創(chuàng)建不關(guān)心其他的。
到這里我們要知道:
第一: AndroidViewModelFactory
在正常情況下是全局單例只有一個,只是一個反射創(chuàng)建對象的工具類。
第二:ViewModelProvider
是每次獲取創(chuàng)建ViewModel
的時候都會創(chuàng)建一個新的湘换。
第三:ViewModelStore
是每一個Activity或者Fragment都有一個仰担。
關(guān)注ViewModel
創(chuàng)建:
//viewModelProvider的get方法
viewModelProvider.get(GirlsViewModel.class);
會用 DEFAULT_KEY 和 類名組成一個key值去獲取,接著向下看:
代碼很簡單玷氏,流程如下:
(1) 先從mViewModelStore
中使用key去獲取ViewModel
, mViewModelStore
中是使用HashMap
去存儲一個Activity
或者Fragment
的ViewModel
的堵未。如果獲取到就返回。
(2) 沒獲取到就使用單例mFactory
的create方法反射創(chuàng)建ViewModel
,create方法的代碼在上面貼出來了盏触。
(3) 使用Key存入mViewModelStore
并返回渗蟹。
到這里ViewModel
的創(chuàng)建基本就是講完了,但是可能還是有些懵逼赞辩。下面接著看吧雌芽。
一句話總結(jié)ViewModel
是怎么被創(chuàng)建的:
創(chuàng)建一個ViewModelProvider
,使用ViewModelProvider
內(nèi)部的全局單例AndroidViewModelFactory
來反射創(chuàng)建 ViewModel
,并把創(chuàng)建的ViewModel
存入傳入的ViewModelStore
中辨嗽!
二世落、ViewModel是怎么存儲的?
存儲就是要講解ViewModelStore
了糟需。
直接看源代碼:
代碼就這數(shù)的清的幾行屉佳,就是一個
HashMap
用存儲ViewModel谷朝。提供get
,put
,clear
三個方法。上面說了
ViewModelStore
是每一個Activity
或者ViewModel
都有一個的武花,當Activity
或者Fragment
銷毀的時候就會調(diào)用clear
方法了圆凰。
ViewModelStore
被誰創(chuàng)建,被誰持有体箕?-------------------> +_+
搶答:被HolderFragment
創(chuàng)建和持有专钉!
HolderFragment
跟我們的Activity或者Fragment有什么關(guān)系?-------------------> +_+
搶答:當我們要給Activity或者Fragment創(chuàng)建ViewModel的時候累铅,系統(tǒng)就會為Activity或者Fragment添加一個HolderFragment
跃须,HolderFragment
中會創(chuàng)建持有一個ViewModelStore
。
HolderFragment
怎么創(chuàng)建怎么被添加争群?-------------------> +_+
這一步其實可以分解為下面的的樣子:
/**為Activity或者Fragment創(chuàng)建ViewModelStore*/
ViewModelStore viewModelStore = ViewModelStores.of(activity);
/**為本次的ViewModel獲取創(chuàng)建一個ViewModelProvider*/
ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore, factory);
完成了上面兩步才可以這樣: viewModelProvider.get(想要的ViewModel.class);
正是
/**為Activity或者Fragment創(chuàng)建ViewModelStore*/
ViewModelStore viewModelStore = ViewModelStores.of(activity / fragment);
這步為我們的 activity / fragment 注入了一個HolderFragment
回怜,創(chuàng)建HolderFragment
的時候會創(chuàng)建的時候會創(chuàng)建一個ViewModelStore實例,到這里也解釋了一下 ViewModelStore被誰創(chuàng)建换薄,被誰持有玉雾?的問題。
上面我有提過ViewModelStoreOwner
這個接口轻要,其實我們這里說的HolderFragment
就是實現(xiàn)了這個接口的Fragment复旬。
一句話總結(jié)ViewModel
是怎么被存儲的:
這是上面一句話總結(jié)ViewModel
的創(chuàng)建:
這句創(chuàng)建總結(jié)其實也說明了
ViewModel
的存儲。進一步解釋:
ViewModel
是存儲在當前Activity / Fragment
的 HolderFragment
中的ViewModelStore
的HashMap中冲泥,我們可以get
,put
或者在Activity / Fragment
銷毀的時候HolderFragment
會跟隨銷毀驹碍,在HolderFragment
的onDestroy
方法中調(diào)用mViewModelStore
的clear
方法。三凡恍、ViewModel為什么可以實現(xiàn)旋轉(zhuǎn)屏幕不銷毀志秃?
ViewModel的創(chuàng)建獲取方式是: 為Activity / Fragment
創(chuàng)建一個ViewModelStore
,獲取到AndroidViewModelFactory
單例嚼酝,用這個兩個數(shù)據(jù)創(chuàng)建一個ViewModelProvider
,在創(chuàng)建的ViewModelProvider
中可以get我們要的ViewModel浮还。
為Activity / Fragment
創(chuàng)建一個ViewModelStore
,就是調(diào)用下面的方法:
holderFragmentFor(activity)源碼:
holderFragmentFor(activity)方法每一步都有解釋闽巩,很詳細:
HolderFragment holderFragmentFor(FragmentActivity activity) {
//獲取Activity的FragmentManager
FragmentManager fm = activity.getSupportFragmentManager();
//通過HOLDER_TAG在FragmentManager中需要HolderFragment
HolderFragment holder = findHolderFragment(fm);
//獲得的HolderFragment不為空就返回
if (holder != null) {
return holder;
}
//在Map<Activity, HolderFragment>緩存中獲取HolderFragment
//Activity為key钧舌,所以每一個Activity或者Fragment只會有一個HolderFragment
holder = mNotCommittedActivityHolders.get(activity);
//不為空就返回
if (holder != null) {
return holder;
}
//在Application中注冊一個所有Activity生命周期回調(diào)監(jiān)聽,這里只會注冊一次
//這里注冊Activity生命周期監(jiān)聽的目的是在Activity銷毀的時候好移除Map<Activity, HolderFragment>中的對應數(shù)據(jù)
if (!mActivityCallbacksIsAdded) {
mActivityCallbacksIsAdded = true;
activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
}
//new HolderFragment()并通過fm添加到Activity并返回
holder = createHolderFragment(fm);
//添加到Map<Activity, HolderFragment>緩存
mNotCommittedActivityHolders.put(activity, holder);
//返回
return holder;
}
createHolderFragment()方法重點關(guān)注一下:
就是創(chuàng)建了一個
HolderFragment
涎跨,使用傳入的FragmentManager
添加進去洼冻!
這是上面調(diào)用的構(gòu)造方法:
被設(shè)置setRetainInstance(true)
后的Fragment
添加到Activity
中去了,會怎么樣隅很?
setRetainInstance(boolean) 是
Fragment
中的一個方法撞牢。將這個方法設(shè)置為true就可以使當前Fragment在Activity重建時存活下來, 如果不設(shè)置或者設(shè)置為 false, 當前 Fragment 會在 Activity 重建時同樣發(fā)生重建, 以至于被新建的對象所替代。
在setRetainInstance(boolean)為true的 Fragment (就是HolderFragment
)中放一個專門用于存儲ViewModel
的Map, 這樣Map中所有的ViewModel都會幸免于Activity的配置改變導致的重建,讓需要創(chuàng)建ViewModel
的Activity, Fragment都綁定一個這樣的Fragment(就是HolderFragment
), 將ViewModel存放到這個 Fragment 的 Map 中, ViewModel 組件就這樣實現(xiàn)了普泡。
實現(xiàn)原理就是巧妙滴借用了Fragment的setRetainInstance(true)屬性播掷。關(guān)于setRetainInstance更多介紹可以參考:Android應用開發(fā):Fragment的非中斷保存setRetaineInstance
我這里可以展示一下setRetainInstance(true)屬性對生命周期的影響,在一個Activity中加入一個具有setRetainInstance(true)屬性的Fragment:
三秒鐘后展示出了我們的Fragment撼班,生命周期如下:
然后我們點擊模擬器的旋轉(zhuǎn)屏幕按鈕:
生命周期變化:
可以看到Activity因為配置改變了歧匈,調(diào)了onDestroy
方法,但是我們的setRetainInstance(true)
屬性的Fragment
沒有調(diào)用onDestroy
方法砰嘁,說明Fragment得以幸存下來了件炉。
退出當前Activity但是不退出應用后的生命周期:
這時候Activity和Fragment的
onDestroy
生命周期方法先后被調(diào)用了。
在我的HolderFragment
的onDestroy
方法中矮湘,會調(diào)用mViewModelStore
中所有ViewModel
的onCleared
方法斟冕。
四、總結(jié):
關(guān)于ViewModel的實現(xiàn)結(jié)構(gòu)圖可以參考如下:圖片來源https://blog.csdn.net/zhuzp_blog/article/details/78910535
1.ViewModel
以鍵值對的形式存在Activity或者Fragment的HolderFragment
的
ViewModelStore
的HashMap中缅阳。
2.一個Activity或者Fragment可以有很多個ViewModel
磕蛇。
3.一個Activity或者Fragment只會有一個HolderFragment
。
4.Activity或者Fragment的HolderFragment
會保存在全局單例的HolderFragmentManager
的HashMap中十办,在Activity或者Fragment銷毀的時候會移除HashMap中對應的value秀撇。
5.因為ViewModel
是以Activity或者Fragment為存在基礎(chǔ),所以ViewModel
可以在當前Activity和Fragment中實現(xiàn)數(shù)據(jù)共享向族,前提是傳入相同的key值呵燕。