Jetpack 組件之 ViewModel 使用與淺析
ViewModel 是什么?
官方解釋:
The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.
個人理解:
ViewModel 是 UI 和數(shù)據(jù)的橋接層,承擔(dān) UI 層(Activity / Fragment) 的數(shù)據(jù)處理邏輯,同時擁有維護自己的生命周期,而且可以在屏幕旋轉(zhuǎn)時仍保存數(shù)據(jù)。下面是官方提供的一張屏幕旋轉(zhuǎn)時 ViewModel 的生命周期圖。

怎么使用 ViewModel?
這里以 Activity 中使用為例。
自定義 ViewModel 類,繼承自 ViewModel。該類將用作數(shù)據(jù)處理,并返回數(shù)據(jù)給UI
// 這里僅是示例,推薦結(jié)合 LiveData 一起使用。
class CustomViewModel: ViewMdoel(){
private val data: List<String> = mutableListOf()
fun getData(): List<String> {
retrun data
}
}
在 Activity 中獲取自定義的 ViewModel 類,調(diào)用相應(yīng)方法獲取處理后的數(shù)據(jù)
//這里以 Activity 示例,F(xiàn)ragment 類似。
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// 通過ktx 獲取自定義 ViewModel 類
// 需要添加依賴 implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:$viewmodel_version'
//val viewModel: CustomViewModel by viewModels()
// 通過“傳統(tǒng)方式” 獲取自定義 ViewModel 類
val viewModel = ViewModelProvider(this).get(CustomViewModel::class.java)
//獲取數(shù)據(jù),實現(xiàn)后續(xù)UI邏輯
val data = viewModel.getData()
}
}
ViewModel 的原理是什么?
原理僅講解 Activity 部分。源碼部分基于 lifecycle 2.2.0 版本 與 acitivity 1.1.0 版本。
源碼分析分為兩部分,先從調(diào)用方法出發(fā),大體知道內(nèi)部邏輯,再從疑問入手,解答心里疑惑。
實現(xiàn)原理
從調(diào)用方法出發(fā)
//通過 ViewModelProvider#get() 獲取自定義 ViewModel 對象,然后就可以調(diào)用相關(guān)方法
ViewModelProvider(this).get(CustomViewModel::class.java)
通過調(diào)用方法可以發(fā)現(xiàn),需要了解下 ViewModelProvider() 和 get() 方法的內(nèi)部實現(xiàn)。
ViewModelProvider() 內(nèi)部實現(xiàn):
public class ViewModelProvider {
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
// ... 省略 ...
// 入?yún)?nbsp;ViewModelStoreOwner,通過 owner 獲取 ViewModelStore 和 ViewModelFactory
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
/**
* Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
* {@code Factory} and retain them in the given {@code store}.
*
* @param store {@code ViewModelStore} where ViewModels will be stored.
* @param factory factory a {@code Factory} which will be used to instantiate
* new {@code ViewModels}
*/
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
// ... 省略 ...
}
通過上面代碼可以發(fā)現(xiàn),ViewModelProvider 內(nèi)部維護的 ViewModelStore 和 Factory 都是通過傳入的 owner 創(chuàng)建的。根據(jù)調(diào)用傳入的 this 可以知道,此時的owner 為 Activity,那么看下 Activity 中是怎么實現(xiàn)創(chuàng)建的。
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory {
// ... 省略 ...
@NonNull
@Override
public ViewModelStore getViewModelStore() {
// ... 省略 ...
if (mViewModelStore == null) {
// 從上次配置發(fā)生更改(例如屏幕旋轉(zhuǎn))中獲取
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
// 直接創(chuàng)建
// ViewModelStore 內(nèi)部只有一個 Map 來存儲創(chuàng)建的 ViewModel
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
// ... 省略 ...
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
// ... 省略 ...
// 這里判斷是否有創(chuàng)建過,沒有則直接創(chuàng)建。
if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
// ... 省略 ...
}
通過上面代碼可以知道,ComponentActivity 實現(xiàn) ViewModelStoreOwner 和 HasDefaultViewModelProviderFactory 接口,分別創(chuàng)建了 ViewModelStore 和 SavedStateViewModelFactory 對象。ViewModelStore 內(nèi)部維護了一個Map,用于存儲創(chuàng)建的 ViewModel。SavedStateViewModelFactory 內(nèi)部是維護了 ViewModel 的創(chuàng)建,是一個工廠類。Ok,ViewModelProvider 構(gòu)造函數(shù)的內(nèi)部邏輯到此為止。
ViewModelProvider#get() 內(nèi)部實現(xiàn):
public class ViewModelProvider {
private static final String DEFAULT_KEY =
"androidx.lifecycle.ViewModelProvider.DefaultKey";
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
// ... 省略 ...
@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);
}
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//從緩存中獲取
ViewModel viewModel = mViewModelStore.get(key);
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.
}
}
// 緩存沒有,則進行創(chuàng)建。
// 根據(jù)上面 "ViewModelProvider() 內(nèi)部實現(xiàn)" 可以得知,mFactory = new SavedStateViewModelFactory(),
// 而 SavedStateViewModelFactory 實現(xiàn)的 ViewModelProvider.KeyedFactory 接口。
// 所以此時 (mFactory instanceof KeyedFactory) = true
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
// 將新創(chuàng)建的 ViewModel 存入緩存中
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
// ... 省略 ...
}
通過上面代碼可以得知,get() 方法內(nèi)部先從 ViewModelStore 中獲取是否已經(jīng)創(chuàng)建過,如果沒有創(chuàng)建過,則通過 SavedStateViewModelFactory#create() 方法創(chuàng)建,并將新創(chuàng)建的 ViewModel 存入 ViewModelStore 中。
那么接下來就看下 SavedStateViewModelFactory#create() 內(nèi)部實現(xiàn)。
public final class SavedStateViewModelFactory extends ViewModelProvider.KeyedFactory {
private final Application mApplication;
private final ViewModelProvider.AndroidViewModelFactory mFactory;
private final Bundle mDefaultArgs;
private final Lifecycle mLifecycle;
private final SavedStateRegistry mSavedStateRegistry;
// ... 省略 ...
@SuppressLint("LambdaLast")
public SavedStateViewModelFactory(@NonNull Application application,
@NonNull SavedStateRegistryOwner owner,
@Nullable Bundle defaultArgs) {
mSavedStateRegistry = owner.getSavedStateRegistry();
mLifecycle = owner.getLifecycle();
mDefaultArgs = defaultArgs;
mApplication = application;
mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
// 判斷是否ViewModel繼承自 AndroidViewModel,由于咱們自定義的 ViewModel 繼承自 ViewModel,所以這里不成立
boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
Constructor<T> constructor;
if (isAndroidViewModel) {
constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
} else {
// 內(nèi)部判斷傳入的 Class 構(gòu)造函數(shù)是否包含指定參數(shù)。
// 此時咱們自定義的 ViewModel 構(gòu)造函數(shù)未包含所有任何參數(shù),所以這里返回 null
constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
}
if (constructor == null) {
// 通過上面步驟可以的值,最后調(diào)用 mFactory#create() 進行創(chuàng)建。
return mFactory.create(modelClass);
}
// ... 省略 ...
}
// ... 省略 ...
}
通過上面代碼可以發(fā)現(xiàn),SavedStateViewModelFactory#create() 內(nèi)部做了一些校驗,但是咱們自定義的 ViewModel 都沒有滿足,最后通過 ViewModelProvider.AndroidViewModelFactory.getInstance(application)#create() 來創(chuàng)建。那么就看看 AndroidViewModelFactory#create() 內(nèi)部實現(xiàn)。
public class ViewModelProvider {
// ... 省略 ...
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
// ... 省略 ...
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
// 判斷是否ViewModel繼承自 AndroidViewModel。咱們自定義的 ViewModel 繼承自 ViewModel。
// 所以最后調(diào)用 super.create(),即 ViewModelProvider.NewInstanceFactory#create()
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
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);
}
}
// ... 省略 ...
public static class NewInstanceFactory implements Factory {
// ... 省略 ...
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
try {
// 直接通過 Class.newInstance() 進行創(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);
}
}
}
// ... 省略 ...
}
通過上面代碼可以知道,AndroidViewModelFactory#create 又進行了校驗,最后調(diào)用到了 NewInstanceFactory#create() 方法,該方法直接通過 Class.newInstance() 創(chuàng)建了 ViewModel。至此,ViewModel 的創(chuàng)建過程就走完了。在文章的最后會對該過程有一個總結(jié)。
從疑問出發(fā)
1. ViewModel 是怎么實現(xiàn)屏幕旋轉(zhuǎn)時仍可保留數(shù)據(jù)的?
關(guān)于屏幕旋轉(zhuǎn)仍可保留數(shù)據(jù)的重點,其實需要知道 Activity#onRetainNonConfigurationInstance(),通過該方法的注釋可以知道,當(dāng)有新配置導(dǎo)致重新創(chuàng)建實例時,系統(tǒng)會通過該方法回調(diào)通知。
public class Activity{
// ... 省略 ...
/**
* Called by the system, as part of destroying an
* activity due to a configuration change, when it is known that a new
* instance will immediately be created for the new configuration. You
* can return any object you like here, including the activity instance
* itself, which can later be retrieved by calling
* {@link #getLastNonConfigurationInstance()} in the new activity
* instance.
*
* ... 省略 ...
*
*/
public Object onRetainNonConfigurationInstance() {
return null;
}
// ... 省略 ...
}
ComponentActivity 重寫了該方法,當(dāng)屏幕旋轉(zhuǎn)時將 viewModelStore 保存其中,這樣當(dāng)屏幕旋轉(zhuǎn),重新走 getViewModelStore() 時,先通過 getLastNonConfigurationInstances() 方法就可以獲取到之前的 viewModelStore。
PS: 這里需要注意一點,ComponentActivity#onRetainNonConfigurationInstance() 提到了該方法禁止自己重寫!
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory {
// ... 省略 ...
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mViewModelStore == null) {
// 從上次配置發(fā)生更改(例如屏幕旋轉(zhuǎn))中獲取
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
// ... 省略 ...
/**
* Retain all appropriate non-config state. You can NOT
* override this yourself! Use a {@link androidx.lifecycle.ViewModel} if you want to
* retain your own non config state.
*/
@Override
@Nullable
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;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
// ... 省略 ...
}
2. ViewModel 是怎么管理生命周期,在 Activity 銷毀時清理數(shù)據(jù)的?
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory {
// ... 省略 ...
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
// ... 省略 ...
// 監(jiān)聽 Activity 的生命周期變化,當(dāng)收到 Event = ON_DESTROY && 未發(fā)生配置更新時,此時為真正銷毀,清理所有數(shù)據(jù)。
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
// ... 省略 ...
}
// ... 省略 ...
}
通過上面代碼可以發(fā)現(xiàn),依賴于 Lifecycle 提供的生命周期事件,通過 addObserver(),監(jiān)聽 Activity 的生命周期,當(dāng)接收到 ON_DESTROY 事件并且不是因為配置更新(屏幕旋轉(zhuǎn))導(dǎo)致的 destroy 時,調(diào)用 ViewModelStore#clear() 清理所有數(shù)據(jù)。
3. 單Activity多Fragment時,怎么做到Fragment之間共享數(shù)據(jù)的?
通過源碼分析這個問題其實已經(jīng)有結(jié)論了。當(dāng)調(diào)用 ViewModelProvider() 方法傳入的是 getActivity(), ViewModelStore 和 Factory 的創(chuàng)建都是在 Activity 中,所以 ViewModel 的數(shù)據(jù)也是在 Activity 中,所以相當(dāng)于用 Activity 來維護數(shù)據(jù),然后基于此 Activity 的 Fragment 就都可以訪問這些數(shù)據(jù)。
總結(jié)
簡單總結(jié)下 ViewModel 的實現(xiàn)原理:調(diào)用 ViewModelProvider() 方法,該方法內(nèi)部通過接口反向依賴 ViewModelStore 和 Factory 的實現(xiàn)(這里即反向依賴 ComponentActivity 來創(chuàng)建)。然后調(diào)用 ViewModelProvider#get() 方法,該方法內(nèi)部先從 ViewModelStore 中獲取緩存,如果沒有則調(diào)用 Factory#create() 進行創(chuàng)建,經(jīng)過各種條件校驗,最終調(diào)用 ViewModelProvider.NewInstanceFactory#create() 方法,通過 Class.newInstance() 創(chuàng)建出來,最后將新創(chuàng)建的 ViewModel 存入 ViewModelStore 中。下面為調(diào)用時序圖,可參照自行走一遍源碼。

