问题
- 配置变更时 ViewModel、onSaveIntanceState 对于数据的保存和恢复有什么区别?
- onSaveInstanceState和ViewModel在数据缓存方面的区别?
- ViewModel 是如何在配置变化重建恢复数据的?
- SaveStateHandle 的作用?
ViewModel 创建
获取实例时通过下面这个方法:
ViewModelProvider(this).get(MyViewModel::class.java)
public ViewModelProvider(ViewModelStoreOwner owner, Factory factory) {
// activity/fragment 是直接创建 ViewModelStore 或者 恢复时系统赋值的
this(owner.getViewModelStore(), factory);
}
public <T extends ViewModel> T get(String key, Class<T> modelClass) {
//ViewModelStore 内部维护了一个用于储存VM的HashMap。
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
// 缓存里有直接结束
return (T) viewModel;
} else {
}
if (mFactory instanceof KeyedFactory) {
//如果无缓存直接通过 factory 创建
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
//然后放到 缓存里
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
// 默认的 factory 创建实例。
private fun <T : ViewModel> create(modelClass: Class<T>, app: Application): T {
return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
try {
modelClass.getConstructor(Application::class.java).newInstance(app)
} catch (e: NoSuchMethodException) {
RuntimeException("Cannot create an instance of $modelClass", e)
}
} else super.create(modelClass)
}
- ViewModelStoreOwner: 接口,AppCompatActivity/Fragment实现了该接口。
- vm 的实例是 ViewModelStore 中拿的,ViewModelStor 内部维护了一个用于储存VM的HashMap。
- ViewModelStore 是被 activity/fragment 持有的,每个 Activity(Fragment) 都会拥有一个 ViewModelStore 实例。
所以关键问题是看 ViewModelStore 创建路径。
ViewModel 的恢复
ComponentActivity getViewModelStore:
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;
}
- 如果有的话直接从NonConfigurationInstances获取。
- 否则,创建一个新的ViewModelStore对象。
NonConfigurationInstances是什么东西?NonConfigurationInstances 用来缓存不受配置更改影响的数据,
同时,我们还可以在ComponentActivity里面看到一段代码:
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();
}
}
}
});
如果Activity是因为配置更改导致onDestroy方法的回调,并不会清空ViewModelStore里面的内容, 即 ViewModel 的缓存。
NonConfigurationInstances 的赋值是在 Activity 的 attach 方法,系统将上次的 NonConfigurationInstances赋值给了新的Activity对象。
ViewModel 的保存
NonConfigurationInstances为啥能保证Activity重建前后,ViewModeStore是同一个对象呢?
performDestroyActivity方法最后会回调到Activity的onDestroy方法,我们可以通过这个方法可以找到ActivtyThread在Activity onDestroy之前做了保存操作。
public final Object onRetainNonConfigurationInstance() {
//配置变化时缓存viewModelStore 到 NonConfigurationInstances
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;
}
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) {
if (getNonConfigInstance) {
// ······
// retainNonConfigurationInstances方法的作用就是创建一个对象
r.lastNonConfigurationInstances= r.activity.retainNonConfigurationInstances();
// ······
}
// Activity的onDestroy方法回调
mInstrumentation.callActivityOnDestroy(r.activity);
return r;
}
配置变化时会回调onRetainNonConfigurationInstance方法,将mViewModelStore保存在NonConfigurationInstances,同时performDestroyActivity时将NonConfigurationInstances记录到ActivityClientRecord中去。
小结:VM -> ViewModelStore->NonConfigurationInstances -> ActivityThread 中的 ActivityClientRecord。中间通过 activity 来处理保存、恢复。
onSaveInstanceState和ViewModel
两者在数据处理方面的不同:
- ViewModel最终是交给 ActivityThread 中的 ActivityClientRecord 暂存的,进程被回收ViewModel 也就被回收。
- ViewModel能恢复因配置变更导致重建的数据,并不能恢复因资源限制(比如内存限制,电量限制等)进程杀死导致的数据恢复。
- onSaveInstanceState 可以保存因系统限制进程被杀或者配置变化的数据,这些数据存储在内存中,而不是硬盘上,由android 系统来管理。
- ViewModel支持保存大量和复杂的数据,比如说RecyclerView的data(ActivityThread持有)。
SavedStateHandle
ViewModel 通过SavedStateHandle类处理数据与onSaveInstanceState()的方式没有任何区别
class MyViewModel(private val saveStateHandle: SavedStateHandle) : ViewModel() {
val info = saveStateHandle.get<String>("KEY_INFO") // 取值
val infoFlow = saveStateHandle.getStateFlow("KEY_INFO", "") // 以 flow 形式
fun setInfo(info: String) { // 设置值
saveStateHandle.set("KEY_INFO", info)
}
}
//通过AbstractSavedStateViewModelFactory或者SavedStateViewModelFactory创建ViewModel对象:
private val viewModel by lazy {
ViewModelProvider(
this, SavedStateViewModelFactory(application, this)
)[MyViewModel::class.java]
}
比如在Fragment中是如何save的?
// 触发销毁时
void performSaveInstanceState(Bundle outState) {
......
onSaveInstanceState(outState);
mSavedStateRegistryController.performSave(outState);
......
}
//也是保存到bundle里
void performSave(@NonNull Bundle outBundle) {
Bundle components = new Bundle();
if (mRestoredState != null) {
components.putAll(mRestoredState);
}
for (Iterator<Map.Entry<String, SavedStateProvider>> it =
mComponents.iteratorWithAdditions(); it.hasNext(); ) {
Map.Entry<String, SavedStateProvider> entry1 = it.next();
components.putBundle(entry1.getKey(), entry1.getValue().saveState());
}
outBundle.putBundle(SAVED_COMPONENTS_KEY, components);
}
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)
PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题