2020年最后一篇,哈哈。
本篇文章主要分析ViewModel在Activity從銷毀到重建時是如何保存并恢復的。
源碼版本:androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0
使用的Android版本是 28
先說下結論
- 當Activity銷毀重建的時候,會調用 ActivityThread 的 performDestroyActivity 方法,保存了一個
Activity.NonConfigurationInstances
對象在 ActivityClientRecord 對象中(ActivityClientRecord 對象應該是在 ActivityThread prepareRelaunchActivity 方法中創建的 )。 - 當重新創建Activity的時候,ActivityThread會調用performLaunchActivity方法。內部會調用Activity的 attach 方法并傳入了 ActivityClientRecord對象中的
Activity.NonConfigurationInstances
對象為為當前Activity實例的mLastNonConfigurationInstances
賦值。 - 接下來在Activity的onCreate方法中,會從Activity的
mLastNonConfigurationInstances.activity
獲取銷毀的Activity保存下來的ViewModelStore
對象。而ViewModel 對象保存了創建過的ViewModel。所以新的Activity獲取到的是老的ViewModel對象。
我們看一下ActivityClientRecord對象中保存的內容就大概清楚了。
基本流程源碼分析
首先創建一個簡單的ViewModel類。然后通過一個例子來對比一下普通對象和ViewModel對象。
class NameViewModel : ViewModel() {
//創建一個持有String類型數據的LiveData
val currentName: MutableLiveData<String> = MutableLiveData("Hello world")
}
簡單使用
class ViewModelActivity : AppCompatActivity() {
//普通的字符串對象
private var normalText = "Hello world"
private lateinit var model: NameViewModel
private lateinit var tvViewModelText: TextView
private lateinit var tvNormalText: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_model)
tvNormalText = findViewById(R.id.tv_normal_text)
tvViewModelText = findViewById(R.id.tv_view_model_text)
//在onCreate中獲取NameViewModel對象。
model = ViewModelProvider(this).get(NameViewModel::class.java)
//給TextView設置text
tvNormalText.text = normalText
tvViewModelText.text = model.currentName.value
//觀察LiveData
model.currentName.observe(this, Observer<String> { t -> tvViewModelText.text = t })
}
fun onClick(view: View) {
model.currentName.value = "Hello world clicked"
normalText = "Hello world clicked"
tvNormalText.text = normalText
}
}
- 開始我們將普通對象normalText和ViewModel中的LiveData持有的字符串都賦值為
Hello world
。 - 點擊按鈕將兩個值都賦值為
Hello world clicked
。 - 旋轉屏幕Activity重建,普通對象normalText被重新初始化為
Hello world
。ViewModel中的LiveData持有的字符串還是Hello world clicked
。
結論:普通對象在Activity由于配置信息發生變化而重新創建的時候,會被重新初始化。ViewModel對象則會保存下來,不會因為Activity重建而重新初始化。
接下來開始分析其中的原理
//獲取ViewModel對象
model = ViewModelProvider(this).get(NameViewModel::class.java)
ViewModelProvider的兩個構造函數。
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
//注釋1處,調用兩個參數的構造函數
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
//注釋2處,Factory用來創建ViewModels
mFactory = factory;
mViewModelStore = store;
}
注釋1處,AppCompatActivity的間接父類ComponentActivity實現了ViewModelStoreOwner
接口。
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner {
private ViewModelStore mViewModelStore;
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// 從NonConfigurationInstances中恢復ViewModelStore
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
//...
}
這里我們先簡單認為ComponentActivity的getViewModelStore()方法,返回了一個ViewModelStore對象。后面還會仔細分析這一部分。
ViewModelProvider的構造函數的注釋1處,AppCompatActivity及其父類都沒有實現HasDefaultViewModelProviderFactory
接口。所以注釋2處傳入的Factory默認是NewInstanceFactory.ViewModelProvider
的一個單例類。
接下來我們看下ViewModelProvider的get方法
private static final String DEFAULT_KEY =
"androidx.lifecycle.ViewModelProvider.DefaultKey";
@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");
}
//根據類名獲取ViewModel對象
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//注釋1處,從ViewModelStore中獲取
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {//類型匹配
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
//直接返回
return (T) viewModel;
} else {
//類型不匹配,丟棄viewModel
if (viewModel != null) {
// TODO: log a warning.
}
}
//注釋2處,根據類對象創建ViewModel對象
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
//加入緩存
mViewModelStore.put(key, viewModel);
//返回創建的對象
return (T) viewModel;
}
邏輯也很簡單,注釋1處根據傳入的modelClass對象的類名稱先從ViewModelStore中獲取對象。
ViewModelStore類:就是一個存放ViewModel的HashMap。
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());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
如果獲取不到,就通過反射創建ViewModel對象并加入到ViewModelStore中來保存。
NewInstanceFactory的create方法,通過反射創建ViewModel對象。
@NonNull
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
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);
}
}
到現在,鋪墊的差不多了,該進入正題了。我們回過頭來看ComponentActivity的getViewModelStore()方法。
核心原理
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mViewModelStore == null) {
//注釋1處
NonConfigurationInstances nc = (NonConfigurationInstances)
getLastNonConfigurationInstance();
if (nc != null) {
//從NonConfigurationInstances中恢復ViewModelStore
mViewModelStore = nc.viewModelStore;
}
//注釋2處,如果mViewModelStore依然為null,則創建新的ViewModelStore對象。
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
注釋1處,調用Activity的getLastNonConfigurationInstance方法。
/**
* 獲取先前由{@link#onRetainNonConfigurationInstance()}方法返回的和配置信息無關
* (non-configuration)的實例數據。
* 當新的Activity的實例的{@link#onCreate}和{@link#onStart}方法被調用的時候,
* 可以調用這個方法來獲取前一個被銷毀的Activity通過{@link#onRetainNonConfigurationInstance()}方法
* 保存的一些數據。
**/
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
如果mLastNonConfigurationInstances
不為null,返回mLastNonConfigurationInstances
的activity
字段,否則返回null。
我們先看一下Activity.NonConfigurationInstances類
static final class NonConfigurationInstances {
//這個activity字段是Object類型
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
我們知道當Activity配置信息發生變化的時候,Activity會銷毀然后重新創建,Activity.NonConfigurationInstances類就是用來在Activity銷毀的時候用來保存一些有用的數據的。和Bundle相比,Activity.NonConfigurationInstances幾乎可以保存任意對象(比如Bitmap)。
接下來,我們看一下Activity.NonConfigurationInstances對象是怎么被保存的。
當Activity因配置信息發生變化銷毀然后重新創建,調用ActivityThread的handleRelaunchActivity方法。
@Override
public void handleRelaunchActivity(ActivityClientRecord tmp, PendingTransactionActions pendingActions) {
//...
handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
//...
}
ActivityThread的handleRelaunchActivityInner方法。
private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
PendingTransactionActions pendingActions, boolean startsNotResumed,
Configuration overrideConfig, String reason) {
//...
//注釋1處
handleDestroyActivity(r.token, false, configChanges, true, reason);
//...
}
注意:注釋1處,傳入的倒數第二個參數為true。
ActivityThread的handleDestroyActivity方法。
@Override
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance, reason);
//...
}
ActivityThread的performDestroyActivity方法。
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (getNonConfigInstance) {
try {
//注釋1處
r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
//...
}
}
}
注釋1處,getNonConfigInstance為true,會調用Activity的retainNonConfigurationInstances方法。
NonConfigurationInstances retainNonConfigurationInstances() {
//注釋1處
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
//fragment 相關要保存的。
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
// We're already stopped but we've been asked to retain.
// Our fragments are taken care of but we need to mark the loaders for retention.
// In order to do this correctly we need to restart the loaders first before
// handing them off to the next activity.
mFragments.doLoaderStart();
mFragments.doLoaderStop(true);
ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
if (activity == null && children == null && fragments == null && loaders == null
&& mVoiceInteractor == null) {
return null;
}
//注釋2處,正常返回一個Activity.NonConfigurationInstances對象
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;
nci.loaders = loaders;
if (mVoiceInteractor != null) {
mVoiceInteractor.retainInstance();
nci.voiceInteractor = mVoiceInteractor;
}
return nci;
}
注釋1處,獲取Activity.NonConfigurationInstances的activity字段要保存的信息。
ComponentActivity重寫了Activity的onRetainNonConfigurationInstance方法。
@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
//獲取當前ViewModelStore對象
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// viewModelStore為null,說明當前Activity實例沒有調用過getViewModelStore()
//來創建ViewModelStore對象,所以檢查前一個Activity銷毀的時候是否在
//NonConfigurationInstances中保存有ViewModelStore對象。
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
//如果沒有需要保存的,就返回null
if (viewModelStore == null && custom == null) {
return null;
}
//注釋1處,正常返回一個ComponentActivity.NonConfigurationInstances對象
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
注釋1處,正常返回一個ComponentActivity.NonConfigurationInstances對象。注意和Activity的Activity.NonConfigurationInstances不一樣。
ComponentActivity.NonConfigurationInstances類
static final class NonConfigurationInstances {
Object custom;
//用來保存創建的ViewModelStore
ViewModelStore viewModelStore;
}
我們回到ActivityThread的performDestroyActivity方法。最終是將一個Activity.NonConfigurationInstances對象保存到了ActivityClientRecord中。這個ActivityClientRecord如下所示:
然后我們看是怎么保存的數據是如何恢復的。
當重新創建Activity的時候,ActivityThread會調用performLaunchActivity方法。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//...
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);
//...
}
調用了Activity的attach方法。并傳入了我們在銷毀上一個Activity的時候保存在ActivityClientRecord中的lastNonConfigurationInstances
對象。
Activity的attach方法
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) {
attachBaseContext(context);
//...
//為當前Activity實例的mLastNonConfigurationInstances賦值。
mLastNonConfigurationInstances = lastNonConfigurationInstances;
}
Activity的attach方法在Activity的onCreate方法之前被調用。在Activity的attach方法內部為當前Activity實例的mLastNonConfigurationInstances賦值。
重新創建的ViewModelActivity的onCreate方法中,我們創建ViewModelProvider會調用ComponentActivity的getViewModelStore方法。
model = ViewModelProvider(this).get(NameViewModel::class.java)
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mViewModelStore == null) {
//注釋1處
NonConfigurationInstances nc =(NonConfigurationInstances)
getLastNonConfigurationInstance();
if (nc != null) {
//從NonConfigurationInstances中恢復ViewModelStore
mViewModelStore = nc.viewModelStore;
}
//注釋2處,如果mViewModelStore依然為null,則創建新的ViewModelStore對象。
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
這個時候,注釋1處會從Activity的mLastNonConfigurationInstances.activity
獲取銷毀的Activity保存下來的mViewModelStore對象。
然后ViewModelProvider的get方法,也就是從mViewModelStore中獲取ViewModel對象。
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//注釋1處,mViewModelStore中獲取
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {//類型匹配
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
//直接返回
return (T) viewModel;
} else {
//類型不匹配,丟棄緩存中的viewModel
if (viewModel != null) {
// TODO: log a warning.
}
}
//注釋2處,根據類對象創建ViewModel對象
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
//加入緩存
mViewModelStore.put(key, viewModel);
//返回創建的對象
return (T) viewModel;
}
注釋1處,這個時候獲取的ViewModel對象不為空,所以不會重新創建ViewModel對象。
總結一下:
當Activity由于配置信息發生改變而重建的時候,會保存一個
Activity.NonConfigurationInstances
對象到ActivityClientRecord中。AppCompatActivity的間接父類ComponentActivity在此過程中保存了一個
ComponentActivity.NonConfigurationInstances
到Activity.NonConfigurationInstances
中。ComponentActivity.NonConfigurationInstances
中保存了AppCompatActivity當前的ViewModelStore
對象。ViewModelStore對象中存儲著當前AppCompatActivity的所有ViewModel對象。在新創建的AppCompatActivity的attach方法中從ActivityClientRecord中獲取了保存的
ViewModelStore
對象。該方法在onCreate方法之前被調用。在新創建的AppCompatActivity的onCreate方法中可以獲取到恢復了的
ViewModelStore
對象。從而可以獲取對應的ViewModel對象。
其他
為啥Activity正常銷毀的時候不保存Activity.NonConfigurationInstances對象?
public class DestroyActivityItem extends ActivityLifecycleItem {
private boolean mFinished;
private int mConfigChanges;
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
//注釋1處,這里的getNonConfigInstance為fasle
client.handleDestroyActivity(token, mFinished, mConfigChanges,
false /* getNonConfigInstance */, "DestroyActivityItem");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
當Activity正常結束,不需要重新創建的時候,的時候會執行DestroyActivityItem
,調用ActivityThread的handleDestroyActivity方法的時候傳入的getNonConfigInstance
參數為false,不會保存Activity.NonConfigurationInstances對象。
ViewModelStore何時清空保存的ViewModel
ComponentActivity的構造函數
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
//注釋1處
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
//...
}
ComponentActivity監聽了聲明周期變化,當ComponentActivity銷毀的時候,如果不是因為配置發生變化而銷毀的話,ViewModelStore就清空保存的ViewModel對象。
參考鏈接: