目錄
問(wèn)題
用過(guò)AAC的人都知道,ViewModel的使用是很簡(jiǎn)單的,沒(méi)有太多復(fù)雜的東西,而ViewModel本身的源碼也異常簡(jiǎn)單。真正“神奇”的地方在于ViewModel的生命周期:

在Activity的configuration(配置)發(fā)生變化時(shí),Activity會(huì)被重建,但是ViewModel卻不會(huì),直至Activity真正消失的時(shí)候,ViewModel的onCleared方法才會(huì)被調(diào)用。這就是我們的核心問(wèn)題,這一切是怎么實(shí)現(xiàn)的?
(源碼版本androidx.lifecycle:lifecycle-viewmodel:2.2.0)
1. ViewModel的獲取
ViewModel本身是異常簡(jiǎn)單的:
public abstract class ViewModel {
protected void onCleared() {
}
}
ViewModel就是這樣,僅僅包含一個(gè)onCeared方法(最初的ViewModel的確只有這一個(gè)方法,后來(lái)又增加了一些方法,但是這些方法都不是給我們使用的,所以可以忽略)。
AAC給我們提供的獲取ViewModel的方法很簡(jiǎn)單:
ViewModelProvider(activity/fragment).get(ViewModel.class) (之前更常見(jiàn)的一種方式是ViewModelProviders.of(activity/fragment).get(ViewModel.class),但是現(xiàn)在這種方式已經(jīng)被刪除了)。
我們來(lái)看一下ViewModelProvider是怎么實(shí)現(xiàn)的:
@SuppressWarnings("WeakerAccess")
public class ViewModelProvider {
private static final String DEFAULT_KEY =
"androidx.lifecycle.ViewModelProvider.DefaultKey";
/**
* 創(chuàng)建ViewModel的Factory
*/
public interface Factory {
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
//這里的ViewModelStoreOwner 一般而言不是Activity就是Fragment,并且它們都實(shí)現(xiàn)了HasDefaultViewModelProviderFactory,也就是說(shuō)Activity/Fragment都會(huì)提供默認(rèn)的ViewModelFactory
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
/**
* @param store ViewModel存儲(chǔ)的地方
* @param factory ViewModel構(gòu)建的地方
*/
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
/**
* 如果不提供key,默認(rèn)的key就是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);
}
/**
* 如果ViewModel在ViewModelStore中已經(jīng)存在,則可以直接返回使用
* 如果不存在,則通過(guò)Factory新建,默認(rèn)的工廠是SavedStateViewModelFactory,新建ViewModel的方式是反射
*/
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//給你一個(gè)機(jī)會(huì)再重新獲取ViewModel時(shí)做一些操作
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
}
總結(jié)一下ViewModelProvider的作用。ViewModelProvider是我們獲取ViewModel的地方,創(chuàng)建ViewModelProvider需要我們提供兩個(gè)參數(shù):1. ViewModelStore 2. ViewModelProvider.Factory。它們的作用也很明了,分別用來(lái)存儲(chǔ)ViewModel和創(chuàng)建ViewModel。首先在ViewModelStore中查找,沒(méi)有的話再通過(guò)Factory創(chuàng)建。
ViewModelProvider的get方法是可以提供一個(gè)key來(lái)說(shuō)明我們需要的是哪個(gè)ViewModel。不過(guò),我們可能不常提供,不提供的話默認(rèn)的key是ViewModel的類名(前面加了一個(gè)前綴)。如果我們的Activity或者Fragment需要同一種ViewModel類的兩個(gè)對(duì)象時(shí),key的作用就很明顯了。
2. ViewModel的創(chuàng)建和存儲(chǔ)
2.1 ViewModelProvider.Factory
默認(rèn)的ViewModelProvider.Factory是SavedStateViewModelFactory,從這個(gè)名字可以看出,這是一個(gè)可以為ViewModel提供SavedState的Factory,ViewModel的SavedState是2.x版本新添加的功能,目的是為了在ViewModel中方便的保存狀態(tài),在Activity重建的時(shí)候使用。不了解這些沒(méi)關(guān)系,因?yàn)槿绻悴皇褂眠@個(gè)SavedState功能的話,這個(gè)SavedStateViewModelFactory其實(shí)會(huì)退化成AndroidViewModelFactory,它只是通過(guò)反射創(chuàng)建ViewModel而已。這要求我們的ViewModel必須有默認(rèn)構(gòu)造函數(shù)。從ViewModel的生命周期圖上可以看出,ViewModel的生命周期要比Activity或者Fragment的更長(zhǎng),ViewModel中自然就不能包含Activity或者Fragment,如果實(shí)在需要在ViewModel中使用Context,那么只能使用ApplicationContext,為了方便我們使用,AAC中還定義了一個(gè)AndroidViewModel:
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
/**
* Return the application.
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
@NonNull
public <T extends Application> T getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}
我們的ViewModel也可以繼承自AndroidViewModel,這樣在ViewModel中就可以方便地使用Application了。而AndroidViewModelFactory也可以通過(guò)反射創(chuàng)建繼承自AndroidViewModel的ViewModel。
想了解更多關(guān)于ViewModel-SavedState的內(nèi)容,可以查看這篇文章ViewModel如何保存狀態(tài)——SavedState源碼解析。
2.2 ViewModelStore
兼容包中的Activity和Fragment皆實(shí)現(xiàn)了ViewModelStoreOwner接口,該接口只包含一個(gè)方法getViewModelStore。
2.2.1 Activity與ViewModelStore
public class ComponentActivity implements ViewModelStoreOwner {
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
// 延遲創(chuàng)建,直到 getViewModelStore() 被調(diào)用時(shí)才會(huì)被創(chuàng)建
private ViewModelStore mViewModelStore;
/**
* 保留所有的 non-config state. 不隨配置變化的那些變量。
* 最重要的就是保留了 ViewModelStore
*/
@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
/**
* 從 NonConfigurationInstances 中取出 viewModelStore
* 沒(méi)有則新建一個(gè) ViewModelStore
*/
@NonNull
@Override
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 nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
}
對(duì)于Activity而言,它本身就包含有一個(gè)ViewModelStore,在調(diào)用getViewModelStore方法時(shí)會(huì)被創(chuàng)建(如果需要的話)。ViewModel最神奇的地方就是它不會(huì)隨配置的變化而重建,表現(xiàn)在生命周期上就是文中的第一幅圖。這一切是怎么實(shí)現(xiàn)的呢?在上述代碼中我們已經(jīng)可以看出來(lái)了。Activity的ViewModelStore會(huì)在配置發(fā)生變化時(shí)通過(guò)onRetainNonConfigurationInstance方法被保留下來(lái),在getViewModelStore方法中又首先會(huì)從NonConfigurationInstances中取出被保留下來(lái)的ViewModelStore(如果有的話)。這樣,ViewModelStore就不會(huì)因?yàn)榕渲玫淖兓亟?,也就是說(shuō),我們的ViewModel不受配置變化的影響。
2.2.2 Fragment與ViewModelStore
public class Fragment implements ViewModelStoreOwner {
//自身并沒(méi)有實(shí)現(xiàn),委托給了FragmentManager
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
return mFragmentManager.getViewModelStore(this);
}
}
再來(lái)看看FragmentManager
final class FragmentManagerImpl extends FragmentManager {
private FragmentManagerViewModel mNonConfig;
//又委托給了mNonConfig
@NonNull
ViewModelStore getViewModelStore(@NonNull Fragment f) {
return mNonConfig.getViewModelStore(f);
}
/**
* 這個(gè)方法會(huì)在上述方法 getViewModelStore 之前被調(diào)用,是mNonConfig被賦值的地方
* 其中host一般情況下是我們的Activity
* parent是指父Fragment
*/
public void attachController(@NonNull FragmentHostCallback host,
@NonNull FragmentContainer container, @Nullable Fragment parent) {
if (mHost != null) throw new IllegalStateException("Already attached");
mHost = host;
mContainer = container;
mParent = parent;
if (parent != null) { //父Fragment不為null,則從父Fragment的FragmentManager中獲取NonConfig
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) {
//host是我們的Activity,它的確實(shí)現(xiàn)了ViewModelStoreOwner接口
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
} else {
mNonConfig = new FragmentManagerViewModel(false);
}
}
}
Fragment將getViewModelStore委托給了其FragmentManager,F(xiàn)ragmentManager又把getViewModelStore委托給了NonConfig。從NonConfig這個(gè)名字可以猜測(cè)出,它應(yīng)該是保存Fragment中與配置無(wú)關(guān)的變量的地方(比如說(shuō)我們的ViewModelStore)。而NonConfig的類型卻是FragmentManagerViewModel,用一個(gè)ViewModel去保存ViewModelStore,這是什么騷操作?

看看FragmentManagerViewModel就知道了
/**
* FragmentManagerViewModel is the always up to date view of the Fragment's non configuration state
* FragmentManagerViewModel保存了最新的Fragment的non configuration state
*/
class FragmentManagerViewModel extends ViewModel {
private static final ViewModelProvider.Factory FACTORY = new ViewModelProvider.Factory() {
@NonNull
@Override
@SuppressWarnings("unchecked")
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
FragmentManagerViewModel viewModel = new FragmentManagerViewModel(true);
return (T) viewModel;
}
};
/**
* 從上一段代碼中可以看出,此處的ViewModelStore其實(shí)就是Activity中的ViewModelStore
* 也就是說(shuō)這個(gè)FragmentManagerViewModel是被保存在Activity的ViewModelStore中的
*/
@NonNull
static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
FACTORY);
return viewModelProvider.get(FragmentManagerViewModel.class);
}
//保存子Fragment的NonConfig,對(duì)于我們而言,其實(shí)就是保存子Fragment的ViewModelStore
private final HashMap<String, FragmentManagerViewModel> mChildNonConfigs = new HashMap<>();
//保存Fragment的ViewModelStore
private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();
@NonNull
FragmentManagerViewModel getChildNonConfig(@NonNull Fragment f) {
FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);
if (childNonConfig == null) {
childNonConfig = new FragmentManagerViewModel(mStateAutomaticallySaved);
mChildNonConfigs.put(f.mWho, childNonConfig);
}
return childNonConfig;
}
@NonNull
ViewModelStore getViewModelStore(@NonNull Fragment f) {
ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
if (viewModelStore == null) {
viewModelStore = new ViewModelStore();
mViewModelStores.put(f.mWho, viewModelStore);
}
return viewModelStore;
}
}
如注釋所說(shuō),F(xiàn)ragmentManagerViewModel的作用是保存Fragment最新的non configuration state,我們要保存的Fragment的ViewModelStore也是non configuration state,保存在FragmentManagerViewModel中正合適。但是,F(xiàn)ragmentManagerViewModel是怎么做到這一點(diǎn)的呢?答案很簡(jiǎn)單,把FragmentManagerViewModel放到Activity的ViewModelStore中,我們上面已經(jīng)分析過(guò)了,Activity的ViewModelStore在配置發(fā)生變化時(shí)會(huì)被保留下來(lái),所以FragmentManagerViewModel自然也就不受配置變化的影響。想想也是,F(xiàn)ragmentManagerViewModel也就是個(gè)普通的ViewModel,自然擁有ViewModel的特點(diǎn),只不過(guò)AAC正好利用了這一點(diǎn),把它用來(lái)保存Fragment的non configuration state(我們最關(guān)心的Fragment的ViewModelStore也在其中)。

Activity的ViewModelStore是不隨配置變化的,F(xiàn)ragment的ViewModelStore也自然不隨配置變化。
2.2.3 ViewModelStore
說(shuō)了這么多ViewModelStore,到底ViewModelStore長(zhǎng)啥樣?顯然ViewModelStore就是存儲(chǔ)ViewModel的地方。如前所述,我們實(shí)際上是通過(guò)字符串key去獲取的ViewModel,那么ViewModelStore自然是以Map的形式來(lái)存儲(chǔ)ViewModel。嗯,基本上這就是ViewModelStore的全部,就是這么簡(jiǎn)單。
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());
}
/**
* 會(huì)在Activity、Fragment destroy并且不是因?yàn)榕渲冒l(fā)生變化的時(shí)候被調(diào)用
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
3. 隱藏技能
- 如果我們需要在同一個(gè)Activity或者Fragment中創(chuàng)建多個(gè)同一類型的ViewModel,我們可以在獲取ViewModel的時(shí)候用不同的
key進(jìn)行區(qū)分。 - 默認(rèn)情況下
AndroidViewModelFactory會(huì)幫我們以反射的方式創(chuàng)建ViewModel,但是ViewModel是在View和Model之間起到橋梁的作用,ViewModel一般情況下都需要包含Model層,而使用AndroidViewModelFactory又要求我們的ViewModel必須包含默認(rèn)構(gòu)造函數(shù)(AndroidViewModel情況除外),這就導(dǎo)致我們不能使用構(gòu)造函數(shù)的方式去注入Model層,有時(shí)候這很不方便。還好,我們可以提供自己的ViewModelProvider.Factory。恰好,dagger2有一種能力叫作MultiBindings,是以依賴注入創(chuàng)建ViewModelProvider.Factory的絕佳方式,詳見(jiàn)當(dāng)Dagger2撞上ViewModel。 - 你可能覺(jué)得Activity的
onRetainNonConfigurationInstance是個(gè)保存NonConfig State的好地方,如果我們也有什么不隨配置變化的變量,也可以利用onRetainNonConfigurationInstance來(lái)保存,甚至Activity中有個(gè)方法叫onRetainCustomNonConfigurationInstance,專門(mén)幫我們保存自己的NonConfig State。恭喜你,從源碼中又學(xué)到了一點(diǎn)沒(méi)有卵用的東西。Activity覺(jué)得你很聰明,并向你扔出一個(gè)Deprecated警告。onRetainCustomNonConfigurationInstance方法已經(jīng)被標(biāo)記為廢棄的,并推薦你使用ViewModel來(lái)保存NonConfig State。至于原因,不言自明。
4. 總結(jié)
ViewModel要解決的核心問(wèn)題是怎樣在配置發(fā)生變化時(shí)保留ViewModel。利用Activity提供的onRetainNonConfigurationInstance方法,可以方便的保留不受配置變化影響的數(shù)據(jù)。
在實(shí)現(xiàn)了Activity的ViewModelStore之后,我們便可以利用ViewModel本身的特點(diǎn)去保存Fragment的ViewModelStore,如此這般,F(xiàn)ragment的ViewModel便也不再受配置變化的影響。用Activity中的ViewModelStore去保存FragmentManagerViewModel,而FragmentManagerViewModel又保存了Fragment的ViewModelStore。嗯,真真是踩在自己的肩膀上向上爬。