官方文檔:https://developer.android.com/topic/libraries/architecture/viewmodel
前言
ViewModel的源碼比較簡單,代碼也不是很多,網(wǎng)上已經(jīng)有了很多的文章來講 ViewModel,但是最近查看源碼時發(fā)現(xiàn)源碼中狀態(tài)保存的實現(xiàn)邏輯有了較大的變化,所以還是記錄下來,并重新梳理了一遍源碼的分析過程。
PS:本文是基于 android.arch.lifecycle:viewmodel:1.1.1 的源碼進(jìn)行分析
簡單示例
class CountLiveData : LiveData<Int>() {
private var count = 0
fun doCount() {
value = ++count
}
}
class MyViewModel : ViewModel() {
var data = CountLiveData()
fun doCount() {
data.doCount()
}
}
class ViewModelDemoActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_demo_view_model)
// 獲取 ViewModel 對象
val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
// 監(jiān)聽數(shù)據(jù)源
viewModel.data.observe(this, Observer { tv_test.text = it.toString() })
// 點(diǎn)擊按鈕改變數(shù)據(jù)
btn_test.setOnClickListener {
viewModel.doCount()
}
}
}
源碼解析
核心類
- ViewModelProviders
- ViewModelProvider
- ViewModelStore
- FragmentActivity
一、ViewModel的創(chuàng)建
ViewModel的實例化很簡單,代碼如下
val viewModel = ViewModelProviders.of(activity).get(MyViewModel::class.java)
1) ViewModelProviders.of(activity, factory) 方法
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
參數(shù)也可以是Fragment,這里以Activity為例,先來看of方法的第二個參數(shù) Factory
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
* <p>
*
* @param modelClass a {@code Class} whose instance is requested
* @param <T> The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
Factory 是 ViewModelProvider 的內(nèi)部類,定義了一個 create 方法,作用就是創(chuàng)建一個 ViewModel 對象,這個很好理解。Factory 接口默認(rèn)有兩個實現(xiàn)類,分別是 NewInstanceFactory 和 AndroidViewModelFactory,其中 NewInstanceFactory 的實現(xiàn)就是通過反射調(diào)用構(gòu)造器創(chuàng)建 ViewModel 對象,AndroidViewModelFactory 則是 NewInstanceFactory 的子類,在 NewInstanceFactory 的基礎(chǔ)上多一個 Context 字段,以下是兩個類的源碼。
- NewInstanceFactory 源碼
public static class NewInstanceFactory implements Factory {
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
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 源碼
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
/**
* Retrieve a singleton instance of AndroidViewModelFactory.
*
* @param application an application to pass in {@link AndroidViewModel}
* @return A valid {@link AndroidViewModelFactory}
*/
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
/**
* Creates a {@code AndroidViewModelFactory}
*
* @param application an application to pass in {@link AndroidViewModel}
*/
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
@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);
}
}
我們繼續(xù)回來看 ViewModelProviders.of(activity, factory) 方法,不用往上翻了,我再貼一下源碼:
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
這里 factory 如果為 null,則會創(chuàng)建一個默認(rèn)的 Factory 對象,類型就是上面講到的 AndroidViewModelFactory。最后,of 方法返回了一個通過構(gòu)造方法創(chuàng)建的 ViewModelProvider 對象,第一個參數(shù)是 activity.getViewModelStore(),這里的activity 是 FragmentActivity 類型
public class FragmentActivity extends ComponentActivity implements
ViewModelStoreOwner,
ActivityCompat.OnRequestPermissionsResultCallback,
ActivityCompat.RequestPermissionsRequestCodeValidator {
@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) {
mViewModelStore = new ViewModelStore();
}
return mViewModelStore;
}
}
在 FragmentActivity 的源碼中可以看到,F(xiàn)ragmentActivity 實現(xiàn)了
ViewModelStoreOwner 接口,接口中定義了 getViewModelStore() 方法,F(xiàn)ragmentActivity 實現(xiàn) getViewModelStore() 方法并返回了一個 ViewModelStore 對象。
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
在 ViewModelStore 中,定義了一個 HashMap 對象,存儲的 key 是包含各 ViewModel 子類完整類名的字符串(在后面的源碼中會看到),value 則是 ViewModel 對象。所以 ViewModelStore 其實就是一個容器,內(nèi)部保存了所有的 ViewModel 實例。
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);
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
2) ViewModelProvider 類
ViewModelProviders.of() 方法最后創(chuàng)建并返回了一個 ViewModelProvider 對象,那我們就繼續(xù)來看 ViewModelProvider 類,首先來看它的構(gòu)造方法:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
在構(gòu)造方法中保存了 ViewModelStore 和 Factory 對象,這兩個參數(shù)在之前都已經(jīng)介紹過了,ViewModelStore 用于保存 ViewModel 對象,F(xiàn)actory 用于創(chuàng)建 ViewModel 對象。
到這里,一個 ViewModelProvider 對象就創(chuàng)建好了,然后我們會調(diào)用 VideModelProvider 的 get 方法來獲取 ViewModel 對象,看源碼:
public class ViewModelProvider {
private static final String DEFAULT_KEY = "android.arch.lifecycle.ViewModelProvider.DefaultKey";
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
// canonicalName 是 modelClass 的完整類名(例如: com.evan.demo.MyViewModel)
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)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
}
get 方法傳入的參數(shù)應(yīng)是 ViewModel 子類的 Class 對象,然后在 mViewModelStore 查看是否有緩存,如果有則直接返回,如果沒有則通過 mFactory 創(chuàng)建一個并存入 mViewModelStore。
到這里為止,我們已經(jīng)讀完了獲取 ViewModel 對象的源碼。
小結(jié):
- 通過 ViewModelProviders.of(activity, factory) 獲取 ViewModelProvider 實例,ViewModelProvider 中包含一個 ViewModelStore 實例,用于保存已創(chuàng)建的 ViewModel 實例(ViewModelStore 通過 FragmentActivity 的 getViewModelStore() 方法獲?。琭actory則是用于創(chuàng)建ViewModel實例
- 通過 ViewModelProvider.get(modelClass) 方法可以獲取 ViewModel實例,實際就是從 ViewModelStore 容器中取出對應(yīng)的 ViewModel實力緩存,如果沒有則通過 factory 創(chuàng)建實例并存入 ViewModelStore
二、ViewModel 實現(xiàn)數(shù)據(jù)持久化原理
還記得么?之前有提到過,ViewModelStore 的實例是由 FragmentActivity 中的 getViewModelStore() 方法提供的,我們再來看源碼:
public class FragmentActivity extends ComponentActivity implements
ViewModelStoreOwner,
ActivityCompat.OnRequestPermissionsResultCallback,
ActivityCompat.RequestPermissionsRequestCodeValidator {
@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) {
mViewModelStore = new ViewModelStore();
}
return mViewModelStore;
}
}
從代碼中可以看到,F(xiàn)ragmentActivity中有一個 mViewModelStore 字段,保存了 ViewModelStore 實例,我們可以在 FragmentActivity 的源碼中對搜索 mViewModelStore,就可以找到幾處對 mViewModelStore 的邏輯處理,這些代碼的位置就是 ViewModel 能夠做到狀態(tài)保存的核心代碼,我們一起來看一下。
/**
* Retain all appropriate fragment state. You can NOT
* override this yourself! Use {@link #onRetainCustomNonConfigurationInstance()}
* if you want to retain your own state.
*/
@Override
public final Object onRetainNonConfigurationInstance() {
// 開發(fā)者自定義的狀態(tài)保存
Object custom = onRetainCustomNonConfigurationInstance();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
if (fragments == null && mViewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = mViewModelStore;
nci.fragments = fragments;
// 返回的 NonConfigurationInstances 對象不會被回收
return nci;
}
上面這段代碼是最核心的部分,大家都知道 Activity 在橫豎屏切換時會回調(diào) onSaveInstanceState 方法用于保存數(shù)據(jù),之后再通過 onCreate 或 onRestoreInstanceState 方法還原數(shù)據(jù)。但是,可能很多人不知道的是,在 Activity 中還有兩個方法也同樣可以用于保存Activity的狀態(tài),它們是 onRetainNonConfigurationInstance 和 getLastNonConfigurationInstance 方法。Activity 在重建時會回調(diào) onRetainNonConfigurationInstance 方法,該方法會返回一個Object對象保存狀態(tài),而不再是Bundle類型了。在 Activity 恢復(fù)時可以調(diào)用 getLastNonConfigurationInstance() 方法獲取最近一次 onRetainNonConfigurationInstance() 方法返回的 Object 對象用于恢復(fù)狀態(tài)。
我們可以看到在源碼中 onRetainNonConfigurationInstance 方法返回的是一個 NonConfigurationInstances 對象,NonConfigurationInstances 類很簡單,只保存了三個對象,分別是custon(自定義狀態(tài))、viewModelStore、fragments(Activity中的所有Fragment的保存狀態(tài)),既然 viewModelStore 跟著 NonConfigurationInstances 實例一起在 onRetainNonConfigurationInstance 方法中返回了,viewModelStore 自然也就被保留了下來,不會被在橫豎屏切換時回收。
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
FragmentManagerNonConfig fragments;
}
那既然 viewModelStore 保存了下來,那肯定也會有恢復(fù)狀態(tài)的邏輯,請看以下源碼:
/**
* Perform initialization of all fragments.
*/
@SuppressWarnings("deprecation")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
mFragments.attachHost(null /*parent*/);
super.onCreate(savedInstanceState);
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
// ...
}
可以看到,在 FragmentActivity 的 onCreate 中調(diào)用 getLastNonConfigurationInstance 方法做了相應(yīng)的處理
PS:細(xì)心的同學(xué)應(yīng)該也注意到了 onRetainNonConfigurationInstance 方法源碼中的注釋,官方不允許開發(fā)者直接覆寫onRetainNonConfigurationInstance()方法,應(yīng)該該方法中已經(jīng)了默認(rèn)實現(xiàn),其中就包括 ViewModelStore 實例的保存,如要開發(fā)者需要保存恢復(fù)其他數(shù)據(jù),則應(yīng)該覆寫 onRetainCustomNonConfigurationInstance方法。
三、ViewModel 的資源回收
根據(jù)官方文檔描述 ViewModel的生命周期范圍涵蓋了整個Activity或Fragment的生命周期,包括Activity重建觸發(fā)的生命周期(比如屏幕旋轉(zhuǎn)或配置更改等情況),當(dāng)生命周期結(jié)束時,會回調(diào)onCleared方法,如下圖所示:

FragmentActivity 在 onDestroy 方法中將 mViewModelStore 資源回收了
/**
* Destroy all fragments.
*/
@Override
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
mFragments.dispatchDestroy();
}
最后看 ViewModelStore 的源碼,在 clear 方法中,調(diào)用了所有 ViewModel 實例的 onCleared() 方法
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);
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
至此,ViewModel 源碼解析就結(jié)束了。
總結(jié)
ViewModel 的代碼并不復(fù)雜,其核心代碼其實就是通過 onRetainNonConfigurationInstance 方法進(jìn)行了狀態(tài)保存。