前言
ViewModel 作為 Jetpack 中的明星組件,相信大家都對其有一定的了解。在 Google 的官方介紹中也詳細的羅列了 ViewModel 的優(yōu)點,如:
- 可以提供和管理UI界面數(shù)據(jù)。(將加載數(shù)據(jù)與數(shù)據(jù)恢復(fù)從 Activity or Fragment中解耦)
- 可感知生命周期的組件。
- 不會因配置改變而銷毀。
- 可以配合 LiveData 使用。
- 多個 Fragment 可以共享同一 ViewModel。
- 等等等....
你也可以通過下列兩個視頻,更為詳細的了解 ViewModel:
在本篇文章中,不會講解 ViewModel 的使用方式及使用 ViewModel 的原因,而是著重于講解 ViewModel 的原理。通過閱讀本篇文章你能了解到:
- ViewModel 在 Activity 中的綁定過程。
- ViewModel 在 Activity 中不會因配置改變而銷毀的原理。
- ViewModel 在 Fragment 中的綁定過程。
- ViewModel 在 Fragment 中不會因配置改變而銷毀的原理。
- ViewMode 能在 Fragment 中共享的原理。
希望通過該篇文章,大家能對 ViewModel 有更深入的了解。
ViewModel 與 Activity 的綁定過程
一般情況下使用 ViewModel,我們一般會先聲明自己的 ViewModel,并在 Activity 中的 onCreate 方法中使用 ViewModelProviders 來創(chuàng)建 ViewModel。 如下代碼所示:
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
在谷歌的最新代碼中,不推薦使用
ViweModelProviders(注意是有s的呦),而是直接使用ViewModelProvider的構(gòu)造函數(shù)來創(chuàng)建ViewModelProvider對象。
通過使用 ViewModelProviders 類的 of() 方法,我們會得到一個 ViewModelProvider 對象。如下代碼所示:
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return new ViewModelProvider(activity);
}
ViewModelProvider 類需要我們傳遞 ViewModelStore 與 Factory 對象。其構(gòu)造函數(shù)聲明如下:
//使用ViewModelStoreOwner對象構(gòu)造函數(shù)
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
//使用ViewModelStoreOwner與Factory對象的構(gòu)造函數(shù)
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
//使用ViewModelStore與Factory對象的構(gòu)造函數(shù)
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
在 ViewModelProvider 內(nèi)部,擁有三種類型構(gòu)造函數(shù):
-
(ViewModelStoreOwner owner):- 該構(gòu)造函數(shù)使用 owner 對象的
getViewModelStore()方法來獲取ViewModelStore對象,如果傳入的 owner 對象也實現(xiàn)了HasDefaultViewModelProviderFactory接口時,那么會調(diào)用getDefaultViewModelProviderFactory()方法獲取 Factory。反之,使用內(nèi)部靜態(tài)的NewInstanceFactory對象來創(chuàng)建 Factory 對象。
- 該構(gòu)造函數(shù)使用 owner 對象的
-
(ViewModelStoreOwner owner, Factory factory):- 該構(gòu)造函數(shù)使用 owner 對象的
getViewModelStore()方法來獲取ViewModelStore對象,使用傳遞的 Factory 對象
- 該構(gòu)造函數(shù)使用 owner 對象的
-
(ViewModelStore store, Factory factory):- 使用
ViewModelStore與Factory對象的構(gòu)造函數(shù)
- 使用
Factory 接口介紹
在 ViewModelProvider 中,Factory 主要用于創(chuàng)建 ViewModel,F(xiàn)actory 的聲明如下:
public interface Factory {
/**
* 通過給定的Class對象創(chuàng)建ViewModel對象
* <p>
*
* @param modelClass 所需ViewModel的Class對象
* @param <T> ViewModel的泛型參數(shù)
* @return 新創(chuàng)建的ViewModel對象
*/
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
通過實現(xiàn) Factory 接口,我們可以實現(xiàn)自己想要的工廠以創(chuàng)建所需的 ViewModel。在 Android 中有多個類都實現(xiàn)了該接口(如 KeyedFactory, AndroidViewModelFactory),這里以默認的 NewInstanceFactory 為例:
public static class NewInstanceFactory implements Factory {
private static NewInstanceFactory sInstance;
@NonNull
static NewInstanceFactory getInstance() {
if (sInstance == null) {
sInstance = new NewInstanceFactory();
}
return sInstance;
}
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
try {
//默認使用對應(yīng)ViewModel類無參的構(gòu)造函數(shù)創(chuàng)建實例對象
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 會調(diào)用 ViewModel 的無參構(gòu)造函數(shù)創(chuàng)建實例對象,當然如果你需要在 ViewModel 中使用其他參數(shù),你也可以傳遞自定義的 Factory。
ViewModelStore 介紹
ViewModelStore 內(nèi)部維護了一個 HashMap,其 key 為 DEFAULT_KEY + ViewModel的Class對象底層類規(guī)范名稱,其 value 為對應(yīng) ViewModel 對象。每個 Activity 與 Fragment 都對應(yīng)著一個 ViewModelStore ,用于存儲所需的 ViewModel。ViewModelStore 類聲明如下所示:
DEFAULT_KEY 值為:"androidx.lifecycle.ViewModelProvider.DefaultKey"
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);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
/**
* 當內(nèi)部的 ViewModel 不再使用時,清除所占的內(nèi)存
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
//下面調(diào)用ViewModel的clear方法
vm.clear();
}
mMap.clear();
}
}
Activity 中創(chuàng)建與獲取 ViewModel 流程
ViewModel 最終的創(chuàng)建與獲取,需要 ViewProvider 類調(diào)用 get(Class<T> modelClass)方法(該方法內(nèi)部通過 ViewModelStore 與 Factory 的配合,創(chuàng)建并保存了所需的 ViewModel 對象),具體代碼如下所示:
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);
}
該方法內(nèi)部會調(diào)用 get(String key, Class<T> modelClass) 方法:
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//??根據(jù)key值從ViewModelStore中取對應(yīng)的ViewModel
ViewModel viewModel = mViewModelStore.get(key);
//??判斷所傳入的Class對象是否是ViewModel的Class類或其子類的對象,如果是,直接返回
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
//??如果為null,根據(jù)傳入的Factory創(chuàng)建新的VideModel
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
//??將新的 ViewModel 存入ViewModelStore,并返回
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
在該方法中,會在 ViewModelStore 中根據(jù)傳入的 key 獲取并保存 ViewModel。其具體邏輯如下:
- 根據(jù) key 值從 ViewModelStore 中取對應(yīng)的 ViewModel。
- 判斷所傳入的 Class 對象是否是 ViewModel 的 Class 類或其子類的對象,如果是,直接返回。(當
Object.isInstance(class)接受的參數(shù)為null時,該方法會返回false) - 如果獲取的 ViewModel 為 null,會根據(jù)傳入的 Factory 對象創(chuàng)建新的 VideModel,并將創(chuàng)建好的 ViewModel 放入 ViewModelStore中。
Activity 中創(chuàng)建與獲取 ViewModel 的整體流程如下所示:

ViewModel 在 Activity 中不會因配置改變而銷毀的原理
我們都知道 ViewModel 不會因為 Activity 的配置發(fā)生改變而銷毀,ViewModel 作用域如下所示:

觀察上圖,我相信小伙伴們肯定有如下疑惑:
- 當 Activity 因配置發(fā)生改變時,系統(tǒng)會重新創(chuàng)建一個新的 Activity 。那老的 Activity 中的 ViewModel 是如何傳遞給新的 Activity 的呢?
- ViewModel 又是如何感知配置是否改變,進而判斷是否銷毀的呢?
要解決如上問題,我們需要了解 Android 中數(shù)據(jù)恢復(fù)的方式以及 Activity 生命周期中 ViewModel 實際處理流程。
數(shù)據(jù)恢復(fù)的常見方式
在 Android 系統(tǒng)中,需要數(shù)據(jù)恢復(fù)有如下兩種場景:
- 場景1:資源相關(guān)的配置發(fā)生改變導(dǎo)致 Activity 被殺死并重新創(chuàng)建。
- 場景2:資源內(nèi)存不足導(dǎo)致低優(yōu)先級的 Activity 被殺死。
針對上述場景,分別對應(yīng)三種不同的數(shù)據(jù)恢復(fù)方式。
對應(yīng)場景1,不考慮在清單文件中配置
android:configChanges的特殊情況。
使用 onSaveInstanceState 與 onRestoreInstanceState
使用 onSaveInstanceState 與 onRestoreInstanceState 方法,能處理場景1與場景2的情況。當你的界面數(shù)據(jù)簡單且輕量時,例如原始數(shù)據(jù)類型或簡單對象(比如 String),則我們可以采用該方式。如果你需要恢復(fù)的數(shù)據(jù)較為復(fù)雜,那你應(yīng)該考慮使用 ViewModle + onSaveInstanceState() (為什么要配合使用,會在下文進行講解),因為使用 onSaveInstanceState() 會導(dǎo)致序列化或反序列化,而這,有一定的時間消耗。
onSaveInstanceState() 更為詳細的介紹以及使用,可參考官方文檔:
使用 Fragment 的 setRetainInstance
當配置發(fā)生改變時,F(xiàn)ragment 會隨著宿主 Activity 銷毀與重建,當我們調(diào)用 Fragment 中的 setRetainInstance(true) 方法時,系統(tǒng)允許 Fragment 繞開銷毀-重建的過程。使用該方法,將會發(fā)送信號給系統(tǒng),讓 Activity 重建時,保留 Fragment 的實例。需要注意的是:
- 使用該方法后,不會調(diào)用 Fragment 的
onDestory()方法,但仍然會調(diào)用onDetach()方法 - 使用該方法后,不會調(diào)用 Fragment 的
onCreate(Bundle)方法。因為 Fragment 沒有被重建。 - 使用該方法后,F(xiàn)ragment 的
onAttach(Activity)與onActivityCreated(Bundle)方法仍然會被調(diào)用。
以下示例代碼展示了如何在配置發(fā)生改變時,保留 Fragment 實例,并進行數(shù)據(jù)的恢復(fù)。
public class MainActivity extends AppCompatActivity {
private SaveFragment mSaveFragment;
public static final String TAG_SAVE_FRAGMENT = "save_fragment";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
mSaveFragment = (SaveFragment) fm.findFragmentByTag(TAG_SAVE_FRAGMENT);
// fragment 不為空,是因為配置發(fā)生改變,F(xiàn)ragment 被重建
if (mSaveFragment == null) {
mSaveFragment = SaveFragment.newInstance();
fm.beginTransaction().add(mSaveFragment, TAG_SAVE_FRAGMENT).commit();
}
//獲取保存的數(shù)據(jù)
int saveData = mSaveFragment.getSaveData();
}
}
Fragment :
public class SaveFragment extends Fragment {
private int saveData;
public static SaveFragment newInstance() {
Bundle args = new Bundle();
SaveFragment fragment = new SaveFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//保存當前Fragment實例
setRetainInstance(true);
saveData = 1010;//通過網(wǎng)絡(luò)請求或查詢數(shù)據(jù)庫,賦值需要保存的數(shù)據(jù)
}
@Override
public void onDetach() {
super.onDetach();
}
public int getSaveData() {
return saveData;
}
}
關(guān)于 Fragment 的 setRetainInstance 更多用法與注意事項,可以參看文章
Handling Configuration Changes with Fragments
使用 onRetainNonConfigurationInstance 與 getLastNonConfigurationInstance
在 Activity 中提供了 onRetainNonConfigurationInstance 方法,用于處理配置發(fā)生改變時數(shù)據(jù)的保存。隨后在重新創(chuàng)建的 Activity 中調(diào)用 getLastNonConfigurationInstance 獲取上次保存的數(shù)據(jù)。我們不能直接重寫上述方法,如果想在 Activity 中自定義想要恢復(fù)的數(shù)據(jù),需要我們調(diào)用上述兩個方法的內(nèi)部方法:
onRetainCustomNonConfigurationInstance()getLastCustomNonConfigurationInstance()
注意:
onRetainNonConfigurationInstance方法系統(tǒng)調(diào)用時機介于onStop - onDestory之間,getLastNonConfigurationInstance方法可在onCreate與onStart方法中調(diào)用。
以下代碼展示了,在 Actiity 中恢復(fù)自定義的數(shù)據(jù):
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String name = (String) getLastCustomNonConfigurationInstance();
if (!TextUtils.isEmpty(name)) {
//獲取恢復(fù)后的數(shù)據(jù),執(zhí)行相應(yīng)操作
}
}
//你可以可以在onStart中,獲取恢復(fù)的數(shù)據(jù)
// @Override
// protected void onStart() {
// super.onStart();
// String name = (String) getLastCustomNonConfigurationInstance();
// if (!TextUtils.isEmpty(name)) {
// }
// }
@Nullable
@Override
public Object onRetainCustomNonConfigurationInstance() {
return "AndyJennifer";
}
}
在 Android 3.0 后,官方推薦使用 Fragment#setRetainInstance(true) 的方式進行數(shù)據(jù)的恢復(fù)。之所以推薦這種方式,個人猜測是為了降低 Activity 的冗余,將數(shù)據(jù)恢復(fù)的任務(wù)從 Activity 抽離出來,這更符合單一職責的設(shè)計模式。
幾種數(shù)據(jù)恢復(fù)方式的總結(jié)
通過了解數(shù)據(jù)恢復(fù)的幾種方式,我們能得到如下對比圖:

ViewModel 的恢復(fù)
ViewModel 在官方設(shè)計之初就傾向于在配置改變時進行數(shù)據(jù)的恢復(fù)??紤]到數(shù)據(jù)恢復(fù)時的效率,官方最終采用了 onRetainNonConfigurationInstance 的方式來恢復(fù) ViewModel 。
在 SDK 27 之前,官方一直采用
Fragment#setRetainInstance(true)的方式恢復(fù)數(shù)據(jù)。導(dǎo)致官方修改了其內(nèi)部實現(xiàn)的原因,猜測是因為 Fragment 的坑,程序的擴展性等其他因素。
知道了 ViewModel 的恢復(fù)方式,那現(xiàn)在一起來解決我們之前的疑惑。當 Activity 因配置發(fā)生改變時,系統(tǒng)會重新創(chuàng)建一個新的 Activity 。那老的 Activity 中的 ViewModel 是如何傳遞給新的 Activity ?
在 Androidx 中的 Activity 的最新代碼中,官方重寫了 onRetainNonConfigurationInstance 方法,在該方法中保存了 ViewModelStore (ViweModelStore 中存儲了 ViewModel ),進而也保存了 ViewModel,具體代碼如下所示:
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
//將ViewModel存儲在 NonConfigurationInstances 對象中
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
當新的 Activity 重新創(chuàng)建,并調(diào)用 ViewModelProviders.of(this).get(xxxModel.class) 時,又會在 getViewModelStore() 方法中獲取老 Activity 保存的 ViewModelStore。那么也就拿到了 ViewModel。具體代碼如下所示:
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
//??獲取保存的NonConfigurationInstances,
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
//??從該對象中獲取ViewModelStore
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
ViewModel 何時判斷是否被移除
ViewModel 最重要的特性就是不會在配置發(fā)生改變的時候被移除。其內(nèi)部實現(xiàn)也非常簡單,監(jiān)聽 Activity 聲明周期,在 onDestory 方法被調(diào)用時,判斷配置是否改變。如果沒有發(fā)送改變,則調(diào)用 Activity 中的 ViewModelStore 的 clear() 方法,清除所有的 ViewModel。具體代碼如下所示:
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
//省略更多....
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
//??在配置沒發(fā)生改變且走到onDestory方法時,清除所有的ViewModel
getViewModelStore().clear();
}
}
}
});
}
ViewModel 在 Fragment 的綁定過程
在官方的最新代碼實現(xiàn)中,F(xiàn)ragment 中的 ViewModel 與其宿主 Activity 有著密切的聯(lián)系。要了解 ViewModel 與 Fragment 的綁定過程,我們需要先了解 FragmentManager 與 FragmentManagerViewModel 相關(guān)知識。
FragmentManager 介紹
每個 Fragment 及宿主 Activity (繼承自 FragmentActivity)都會在創(chuàng)建時,初始化一個 FragmentManager 對象,了解 Fragment 中的 ViewModel 與 Activity 的聯(lián)系的關(guān)鍵,就是理清這些不同階級的棧視圖。
下面給出一個簡要的關(guān)系圖:

- 對于宿主 Activity ,
getSupportFragmentManager()獲取的是 FragmentActivity 的 FragmentManager 對象; - 對于 Fragment ,
getFragmentManager()是獲取的父 Fragment (如果沒有,則是 FragmentActivity )的 FragmentManager 對象,而getChildFragmentManager()是獲取自身的 FragmentManager 對象。
FragmentManagerViewModel 介紹
每個 Fragment 創(chuàng)建時,都會創(chuàng)建一個 FragmentManagerViewModel 對象,在該對象中主要存儲其 子Fragment 的 ViewModelStore 與 FragmentManagerViewMoel。具體結(jié)構(gòu)如下所示:

在 FragmentManagerViewModel 中:
- mViewModelStore 是類型為
<String, FragmentManagerViewModel>的 HashMap - mChildNonConfigs 是類型為
<String, ViewModelStore>的 HashMap
上述兩個 Map 對應(yīng)的 Key 值都為 Fragment 的唯一 UUID。該 UUID 會在 Fragment 對象創(chuàng)建時自動生成。也就是每個 Fragment 對應(yīng)唯一 UUID。
ViewModel 在 Fragment 綁定具體流程
ViewModel 與 Fragment 的綁定流程比較復(fù)雜,主要分為三個流程:
- 第一步:在宿主 Activity 創(chuàng)建時,默認會在其
FramgentManager中創(chuàng)建一個 FragmentManagerViewModel。同時將生成的 FragmentManagerViewModel 存儲在其本身的 ViewModelStore 中。同時使用自身的FragmentManager - 第二步:在 Fragment 創(chuàng)建時,從
宿主Activity或父Fragment中的FramgentManager中獲取對應(yīng)的 FragmentManagerViewModel,并使用自身的ChildFragmentManager中mNonConfig變量進行保存。 - 第三步:將 Fragment 中所創(chuàng)建的 ViewModel 與其自身的 ViewModelStore 關(guān)聯(lián) ,并自身的 ViewModelStore 存儲在
mNonConfig所指向的 FragmentManaerViewModel 中的mViewModelStores中。
下面我將結(jié)合源碼對這三個流程進行詳細的介紹。
第一步流程
在宿主 Activity 創(chuàng)建時,默認會在其
FramgentManager中創(chuàng)建一個 FragmentManagerViewModel。同時將生成的 FragmentManagerViewModel 存儲在其本身的 ViewModelStore 中。同時使用自身的FragmentManager
FragmentActivity 中的 onCreate 方法:
protected void onCreate(@Nullable Bundle savedInstanceState){
mFragments.attachHost(null /*parent*/);//??傳入null
//省略更多...
}
mFragments是 FragmentController,內(nèi)部通過 FragmentHostCallback 間接控制 FragmentManager。
該方法最終會執(zhí)行 FragmentActivity 中 FragmentManager 的 attachController 方法:
void attachController(@NonNull FragmentHostCallback<?> host,
@NonNull FragmentContainer container, @Nullable final Fragment parent) {
//省略更多...
if (parent != null) {
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) {
//??走這里
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
} else {
mNonConfig = new FragmentManagerViewModel(false);
}
}
因為傳入的 parent = null,且 Activity 默認實現(xiàn)了 ViewModelStoreOwner 接口,所以會獲取 Activity 中的 ViewModelStore,接著調(diào)用 FragmentManagerViewModel 的 getInstance() 方法:
static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
FACTORY);
return viewModelProvider.get(FragmentManagerViewModel.class);
}
在該方法中,會創(chuàng)建 FragmentManagerViewModel,并將其添加到 Activity 中的 ViewModelStore 中。
整體流程如下所示:

第二步流程
在 Fragment 創(chuàng)建時,從
宿主Activity或父Fragment中的FramgentManager中獲取對應(yīng)的 FragmentManagerViewModel,并使用自身的ChildFragmentManager中mNonConfig變量進行保存。
當 Fragment 與 Activity 關(guān)聯(lián)時,在其 performAttach() 方法中
void performAttach() {
//??又會調(diào)用attachController
mChildFragmentManager.attachController(mHost, new FragmentContainer() {
@Override
@Nullable
public View onFindViewById(int id) {
if (mView == null) {
throw new IllegalStateException("Fragment " + this + " does not have a view");
}
return mView.findViewById(id);
}
@Override
public boolean onHasView() {
return (mView != null);
}
}, this);//??注意這里的this傳入的parent是當前Fragment
//省略更多...
}
該方法會調(diào)用 Fragment 中 ChildFragmentManager 中的 attachController 方法如下所:
void attachController(@NonNull FragmentHostCallback<?> host,
@NonNull FragmentContainer container, @Nullable final Fragment parent) {
//省略更多...
if (parent != null) {
//??因為parent為this,故我們會獲取Activity的FragmentManager
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) {
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
mNonConfig = FragmentManagerViewModel.getInstance(a);
} else {
mNonConfig = new FragmentManagerViewModel(false);
}
}
注意,當 Fragment 是
子Fragment時,parent.fragmentManager 的值為父Fragment 的 FragmentManager,否則為 Activity 中的 FragmentManager。
繼續(xù)追蹤 FragmentManager 下的 getChildNonConfig 方法:
private FragmentManagerViewModel getChildNonConfig(Fragment f){
return mNonConfig.getChildNonConfig(f);
}
mNonConfig 本身為 FragmentManagerViewModel,我們繼續(xù)跟蹤:
FragmentManagerViewModel getChildNonConfig(Fragment f){
FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);
if (childNonConfig == null) {
//??創(chuàng)建Fragment的FragmentManagerViewModel
childNonConfig = new FragmentManagerViewModel(mStateAutomaticallySaved);
mChildNonConfigs.put(f.mWho, childNonConfig);
}
return childNonConfig;
}
在該方法中,會從 Activity 中的 FragmentManagerViewModel 中的 mChildNonConfigs 中獲取 Fragment 的 FragmentManagerViewModel,如果有,直接返回。反之,存入mChildNonConfigs 中。
整體流程如下所示:

第三步流程
將 Fragment 中所創(chuàng)建的 ViewModel 與其自身的 ViewModelStore 關(guān)聯(lián) ,并將該 ViewModelStore 存儲在
mNonConfig所指向的 FragmentManaerViewModel 中的mViewModelStores中。
在 Fragment 中,ViewModelStore 是通過其 FragmentManager 創(chuàng)建與獲取的。具體代碼如所示:
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
return mFragmentManager.getViewModelStore(this);
}
注意,當 Fragment 是
子Fragment時,mFragmentManager的值為 父Fragment 的 FragmentManager,否則為 Activity 中的 FragmentManager。
繼續(xù)追蹤 FragmentManager 下的 getChildNonConfig 方法:
ViewModelStore getViewModelStore(@NonNull Fragment f) {
return mNonConfig.getViewModelStore(f);
}
mNonConfig 本身為 FragmentManagerViewModel,最終會走 getViewModelStore 方法。
ViewModelStore getViewModelStore(@NonNull Fragment f) {
ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
if (viewModelStore == null) {
viewModelStore = new ViewModelStore();
//將創(chuàng)建好的ViewStore,放入FragmentManagerViewModel中
mViewModelStores.put(f.mWho, viewModelStore);
}
return viewModelStore;
}
在該方法中最終會將 Fragment 的 ViewModelStore 存入 FragmentManagerViewModel 中的 mViewModelStores 集合中。
那么 Fragment 的創(chuàng)建并獲取 ViewModel 的流程如下所示:

ViewModel 在 Fragment 中不會因配置改變而銷毀的原理
ViewModel 在 Fragment 中不會因配置改變而銷毀的原因其實是因為其聲明的 ViewModel 是存儲在 FragmentManagerViewModel 中的,而 FragmentManagerViewModel 是存儲在宿主 Activity 中的 ViewModelStore 中,又因 Activity 中 ViewModelStore不會因配置改變而銷毀,故 Fragment 中 ViewModel 也不會因配置改變而銷毀。
當然在 Google 的代碼實現(xiàn)中,也能很好的處理 Fragment 嵌套的情況。在下述例子中展示了 Fragment 嵌套下 ViewModel 存儲的情況。

在上圖中,我們在 Activity 中 分別添加了 Fragment A、B、C。并在 Fragment C 中有嵌套了 Fragment D、E、F。
結(jié)合本篇文章所講解的知識,我們能得到如下結(jié)構(gòu):

從上圖中,我們可以看出,當存在嵌套 Fragment 的情況下,ViewModel 總是以線性的結(jié)構(gòu)進行存儲。在這種結(jié)構(gòu)下,就能讓宿主 Activity 良好的統(tǒng)一管理與所有的 ViewModel。
ViewModel 能在 Fragment 中共享的原理
ViewModel 的另一大特性就是能在 Fragment 中共享數(shù)據(jù)。還是以上圖例:
假如我們想 Fragment D 獲取 Fragment A 中的數(shù)據(jù),那么我們只有在 Activity 中的 ViewModelStore 下添加 ViewModel。只有這樣,我們才能在不同 Fragment 中獲取相同的數(shù)據(jù)。這也是為什么在 Fragment 中使用共享的 ViewModel 時,我們要在調(diào)用ViewModelProvider.of() 創(chuàng)建 ViewModel 時需要傳入 getActivity() 的原因。
具體例子如下所示:
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class FragmentA extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//??傳入的是宿主Activity
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class FragmentD extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//??傳入的是宿主Activity
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
最后
站在巨人的肩膀上,才能看的更遠~