目录
ViewModel 在 Fragment 中不会因配置改变而销毁的原理
为何使用ViewModel:
1、配合MVVM模式可以提供和管理UI界面数据,将用户交互全都放在ViewModel中,Activity or Fragment只负责显示界面
2、可以感知Activity的生命周期
3、配合LiveData共享数据
4、能够防止内存泄漏
5、等等
ViewModel 概览 | App architecture | Android Developers
ViewModel生命周期:
ViewModel 的生命周期与它所属的 Activity 或 Fragment 的生命周期紧密相关,当 Activity/Fragment 首次创建时(如 onCreate()
方法中),通过 ViewModelProvider
创建 ViewModel 实例。
存活条件:ViewModel 会持续存活,直到其关联的 Activity/Fragment 完全销毁(即非配置变更导致的销毁)。
-
关键场景:
-
配置变更(如旋转屏幕):Activity/Fragment 销毁重建,ViewModel 不销毁,数据得以保留。
-
正常退出(如用户按返回键):Activity/Fragment 永久销毁,ViewModel 随之销毁。
-
被系统回收:若 Activity/Fragment 因系统资源不足被回收,ViewModel 也会被销毁。
-
ViewModel 的销毁
-
触发时机:当关联的 Activity/Fragment 永久销毁时(如调用
finish()
或用户导航离开),ViewModel 会调用onCleared()
方法。 -
onCleared()
的作用:-
释放资源(如取消异步任务、关闭数据库连接)。
-
避免内存泄漏(例如清除对 Context 的引用)。
-
如何使用:
创建ViewModel:
class MyViewModel : ViewModel() {
// 使用 MutableLiveData 存储数据
private val _counter = MutableLiveData<Int>(0)
// 对外暴露不可变的 LiveData
val counter: LiveData<Int> get() = _counter
// 业务逻辑方法
fun incrementCounter() {
_counter.value = (_counter.value ?: 0) + 1
}
fun resetCounter() {
_counter.value = 0
}
// ViewModel 销毁时的清理工作
override fun onCleared() {
super.onCleared()
// 释放资源...
}
}
带参数初始化:
默认情况下, ViewModel 通过无参构造函数创建实例对象,当然如果你需要在 ViewModel 中使用其他参数,你也可以传递自定义的 Factory。
// 使用 ViewModelProvider.Factory
class MyViewModelFactory(private val initialValue: Int) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MyViewModel(initialValue) as T
}
}
// 在 Activity 中使用
val factory = MyViewModelFactory(100)
viewModel = ViewModelProvider(this, factory).get(MyViewModel::class.java)
在Activity中的onCreate中使用:
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
在Kotlin中使用activityViewModels()创建ViewModel:
在Fragment中我们可以使用activityViewModels或者viewModels来创建。
class HomeFragment:Fragment() {
val activityViewModel:MainViewModel by activityViewModels()
//或者
val viewModel:MainViewModel by viewModels()
}
ViewModel 在 Fragment 中不会因配置改变而销毁的原理
在 Activity 中提供了 onRetainNonConfigurationInstance
方法,用于处理配置发生改变时数据的保存。随后在重新创建的 Activity 中调用 getLastNonConfigurationInstance
获取上次保存的数据。我们不能直接重写上述方法,如果想在 Activity 中自定义想要恢复的数据,需要调用上述两个方法的内部方法:
-
onRetainCustomNonConfigurationInstance()
-
getLastCustomNonConfigurationInstance()
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String name = (String) getLastCustomNonConfigurationInstance();
if (!TextUtils.isEmpty(name)) {
//获取恢复后的数据,执行相应操作
}
}
//你可以可以在onStart中,获取恢复的数据
// @Override
// protected void onStart() {
// super.onStart();
// String name = (String) getLastCustomNonConfigurationInstance();
// if (!TextUtils.isEmpty(name)) {
// }
// }
@Nullable
@Override
public Object onRetainCustomNonConfigurationInstance() {
return "AndyJennifer";
}
}
在官方代码中重写了 onRetainNonConfigurationInstance 方法,在该方法中保存了 ViewModelStore
(ViweModelStore 中存储了 ViewModel ),进而也保存了 ViewModel,具体代码如下所示:
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;
}
//将ViewModel存储在 NonConfigurationInstances 对象中
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
ViewModel 在 Fragment 中不会因配置改变而销毁的原因其实是因为其声明的 ViewModel 是存储在 FragmentManagerViewModel 中的,而 FragmentManagerViewModel 是存储在宿主 Activity 中的 ViewModelStore 中,又因 Activity 中 ViewModelStore不会因配置改变而销毁,故 Fragment 中 ViewModel 也不会因配置改变而销毁。
ViewModel 能在 Fragment 中共享的原理
如果我们想从 Fragment A 获取 Fragment B 中的数据,那么我们只有在 Activity 中的 ViewModelStore 下添加 ViewModel。这样Fragment A和Fragment B能够共享一个Activity,只有这样,我们才能在不同 Fragment 中获取相同的数据。这也是为什么在 Fragment 中使用共享的 ViewModel 时,我们要在调用ViewModelProvider.of() 创建 ViewModel 时需要传入 getActivity()
的原因。