打開官網,咱們能夠看到 ViewModel 的描述:html
The
ViewModel
class allows data to survive configuration changes such as screen rotations.java
同時還給出了 ViewModel 的生命週期圖:
android
在發生屏幕旋轉時,ViewModel 實例依然存在
,爲了驗證這句話,我寫了一個小 demo 來佐證,示例 demo 以下:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val mainViewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
Log.e("TAG", "onCreate:$mainViewModel")
}
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
Log.e("TAG", "onSaveInstanceState:")
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
Log.e("TAG", "onRestoreInstanceState:")
}
複製代碼
操做步驟很簡單,就是操做屏幕旋轉,佐證下 ViewModel 實例是否還是 Activity 旋轉屏幕以前的實例,打印結果以下:app
2019-08-13 21:45:55.381 E/TAG: onCreate:com.codelang.jetpack.MainViewModel@b02354
2019-08-13 21:46:06.435 E/TAG: onSaveInstanceState:
2019-08-13 21:46:06.608 E/TAG: onCreate:com.codelang.jetpack.MainViewModel@b02354
2019-08-13 21:46:06.617 E/TAG: onRestoreInstanceState:
複製代碼
經過日誌咱們能夠看到,在發生屏幕旋轉時,旋轉以前的 MainViewModel
與旋轉後的 MainViewModel
內存地址一致,驗證了官網對 ViewModel 描述的正確性。ide
咱們知道,在屏幕發生旋轉時,整個 Activity 都會被銷燬和重建,與之所對應的對象和變量也都會被從新初始化,但 ViewModel 的實例並無受之影響,重建以後還是以前的實例,難道 ViewModel 實例被 Activity 以外的某個變量持有?帶着這樣的疑問咱們來跟蹤 ViewModel 的源碼,看看是如何作到這點的。this
咱們來看下 ViewModel 對象的初始化:google
val mainViewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)spa
一、跟蹤 ViewModelProviders.of日誌
public static ViewModelProvider of(@NonNull FragmentActivity activity,@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
// 關鍵點:activity.getViewModelStore()
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
複製代碼
默認會初始化一個 factory,該 factory 是一個反射初始化 MainViewModel::class.java
的工廠類,關鍵點咱們稍後再說,ViewModelProvider 的初始化須要傳入 activity 的 ViewModelStore 和 factory。code
二、跟蹤 get(MainViewModel::class.java)
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
...
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
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;
}
...
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
//三、
viewModel = (mFactory).create(modelClass);
}
//四、
mViewModelStore.put(key, viewModel);
//五、
return (T) viewModel;
}
複製代碼
序列號解釋:
從整個過程來看,ViewModelStore 這個對象很是重要,ViewModel 的存儲與獲取都與和他有關,Activity 銷燬重建也是從 ViewModelStore 中獲取 ViewModel 的實例,而且這個實例一直是同一個對象。
一、咱們來講說關鍵點: activity.getViewModelStore()
FragmentActivity.class
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
FragmentManagerNonConfig fragments;
}
public ViewModelStore getViewModelStore() {
//一、
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;
}
複製代碼
序列號解釋:
二、咱們來看下 getLastNonConfigurationInstance 是如何拿到 FragmentActivity.NonConfigurationInstances
Activity.class
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
複製代碼
mLastNonConfigurationInstances 是 Activity 的一個叫 NonConfigurationInstances 的靜態內部類。
三、咱們來看下 mLastNonConfigurationInstances 是如何初始化的
Activity.class
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window, ActivityConfigCallback activityConfigCallback) {
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
}
複製代碼
在 Activity attach 的時候被外部傳入進來,那麼,是誰觸發的 attach 呢?咱們知道,Activity 的整個建立都是 ActivityThread 來操做的。
四、咱們來看下 ActivityThread
咱們找到觸發 Activity.attach() 的地方
ActivityThread.class
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
...
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
...
mActivities.put(r.token, r);
...
}
複製代碼
咱們注意到 r.lastNonConfigurationInstances
這個對象,這個對象是從 ActivityClientRecord
中取出來的,而且又將 ActivityClientRecord
存儲到了 ActivityThread
的全局變量 mActivities
中。
五、咱們來看看 ActivityClientRecord 是怎麼來的
下面簡潔下調用鏈:
performLaunchActivity -> handleLaunchActivity -> handleRelaunchActivityInner -> handleRelaunchActivity
最終走到了 handleRelaunchActivity 方法:
@Override
public void handleRelaunchActivity(ActivityClientRecord tmp, PendingTransactionActions pendingActions) {
...
//一、經過 Activity 的 token 獲取對應的 ActivityClientRecord
ActivityClientRecord r = mActivities.get(tmp.token);
...
handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
...
}
複製代碼
最終,咱們找到了 ActivityClientRecord 的來源,所以,整個獲取 ViewModelStore 的調用鏈就出來了:
咱們再回到 Activity 的 getViewModelStore 方法,只有在 ViewModelStore 爲空的狀況下才會走這個調用鏈,這麼作的目的是爲了不頻繁走調用鏈才能拿到 ViewModel 的問題,但重建後,ViewModelStore 確定是爲空的,因此,確定是有一個地方只取一次調用鏈拿到 ViewModelStore 對象,結果就是在 onCreate 方法中
FragmentActivity.class
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
NonConfigurationInstances nc =
// 拿到調用鏈的 ViewModelStore 對象
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null && nc.viewModelStore != null && mViewModelStore == null) {
mViewModelStore = nc.viewModelStore;
}
...
複製代碼
既然有獲取,那必然就會有存儲的過程,獲取的源頭是在 ActivityThread 的 mActivities
中,那存儲必然也是在 mActivities
中。
經過 mActivities.get
的方式,在 ActivityThread 中查找相關引用:
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
...
if (getNonConfigInstance) {
try {
// 一、
r.lastNonConfigurationInstances
// 二、
= r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
...
}
}
...
複製代碼
序列號解釋:
仔細看方法,原來是 performDestroyActivity
,也就是說,在重建 Activity 前,會將 ViewModelStore 給保存起來給 ActivityClientRecord,ActivityClientRecord 是存放在 ActivityThread 的全局 mActivities 集合中的,等到重建後,再從 mActivities 中取出 ActivityClientRecord,再把 ViewModelStore 經過 Activity 的 attach 方法再傳入,這也印證了咱們的 demo 和官方的示例圖,不過咱們仍是得來看看 r.activity.retainNonConfigurationInstances();
是怎麼存儲起來的
一、activity.retainNonConfigurationInstances
Activity.class
NonConfigurationInstances retainNonConfigurationInstances() {
//一、
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
...
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;
nci.loaders = loaders;
...
return nci;
}
複製代碼
retainNonConfigurationInstances 是一個建立 Activity.NonConfigurationInstances 的過程,根據上述調用鏈圖可知,Activity.NonConfigurationInstances 會引用 FragmentActivity.NonConfigurationInstances 對象,引用部分在序列號 1 處 onRetainNonConfigurationInstance 方法
二、onRetainNonConfigurationInstance
FragmentActivity.class
@Override
public final Object onRetainNonConfigurationInstance() {
...
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
// 一、
nci.viewModelStore = mViewModelStore;
nci.fragments = fragments;
return nci;
}
複製代碼
到這裏就很清楚了,FragmentActivity.NonConfigurationInstances 會持有 mViewModelStore 對象進行存儲。
咱們來更改下官方的示例圖: