ViewModel以注重生命周期的方式存储和管理界面相关的数据
因配置更改而重新创建 Activity 后,新 Activity 必须页面数据。对于简单的数据,Activity 可以使用 onSaveInstanceState()
方法从 onCreate()
中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。
ViewModel从界面控制器逻辑中分离出视图数据所有权,这种方法简单高效
架构组件为界面控制器提供了 ViewModel
辅助程序类,该类负责为界面准备数据。
自定义一个辅助程序类,继承自ViewModel
。
public class MyViewModel extends ViewModel {
public int number=0;
}
在配置更改期间会自动保留 ViewModel
对象,以便它们存储的数据立即可供下一个 Activity 或 Fragment 实例使用。
Activity中访问
viewModel= new ViewModelProvider(this).get(MyViewModel.class);
binding.textView.setText(String.valueOf(viewModel.number));
binding.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewModel.number++;
binding.textView.setText(String.valueOf(viewModel.number));
}
});
ViewModel
对象存在的时间比视图或 LifecycleOwners
的特定实例存在的时间更长。这还意味着可以更轻松地编写涵盖 ViewModel
的测试,因为它不了解视图和 Lifecycle
对象。ViewModel
对象可以包含 LifecycleObservers
,如 LiveData
对象。但是,ViewModel
对象绝不能观察对生命周期感知型可观察对象(如 LiveData
对象)的更改。 如果 ViewModel
需要 Application
上下文(例如,为了查找系统服务),它可以扩展 AndroidViewModel
类并设置用于接收 Application
的构造函数,因为 Application
类会扩展 Context
。
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), {
item ->
// Update the UI.
});
}
}
两个 Fragment 各自获取 ViewModelProvider
时,它们会收到相同的 SharedViewModel
实例(其范围限定为该 Activity)
一般来说,ViewModel
从创建对象开始存在,直至 activity销毁消失(onClear()
方法)。
向ViewModel
传递数据需要借助ViewModelProvider.Factory
接口
为ViewModel辅助类添加构造器
public class MyViewModel<T extends ViewModel> extends ViewModel {
public int number;
public MyViewModel(int number){
this.number=number;
}
}
新建一个类实现ViewModelProvider.Factory接口
public class MyViewModelFactory implements ViewModelProvider.Factory {
private int num;
public MyViewModelFactory(int num){
this.num=num;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return (T) new MyViewModel(num);
}
}
在create()
方法中创建了一个MyViewModel实例,并传入数据
activity中使用
viewModel= new ViewModelProvider(this, new MyViewModelFactory(num)).get(MyViewModel.class);
传入了一个MyViewModelFactory对象作为参数,并传入了数据,最后返还到MyViewModel中
onSaveInstanceState
的区别从存储位置上来说,ViewModel
是在内存中,因此其读写速度更快,但当进程被系统杀死后,ViewModel
中的数据也不存在了。从数据存储的类型上来看,ViewModel
适合存储相对较重的数据,例如网络请求到的 list 数据,而 onSaveInstanceState
适合存储轻量可序列化的数据
ViewModel
:抽象类,主要有 clear方法,final级,不可修改。开发者可重写 onCleared 方法来自定义数据的清空
ViewModelStore
:内部维护一个 HashMap 以管理 ViewModel
ViewModelStoreOwner
:接口,ViewModelStore
的作用域,实现类为 ComponentActivity
和 Fragment
,此外还有 FragmentActivity.HostCallbacks
,仅有一个方法getViewModelStore()
。
ViewModelProvider
:用于创建 ViewModel
,其构造方法有两个参数,第一个参数传入 ViewModelStoreOwner
,确定了 ViewModelStore
的作用域,第二个参数为 ViewModelProvider.Factory
,用于初始化 ViewModel
对象,默认为 getDefaultViewModelProviderFactory()
方法获取的 factory
ViewModelStoreOwner 持有 ViewModelStore 持有 ViewModel
ViewModel
仍然存在activity 有着 saveInstanceState
机制,但在ViewModel中并未使用该机制。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4TQdwmBJ-1596693689718)(D:\typora\md文档\操作系统\43.png)]
ComponentActivity
实现了 ViewModelStoreOwner
接口,意味着需要重写 getViewModelStore()
方法,该方法为 ComponentActivity
的 mViewModelStore
变量赋值。实际上需求就是Activity重建时,这个 mViewModelStore
变量不变。
由于 ActivityThread
中的 ActivityClientRecord
不受 activity 重建的影响,所以 activity 重建时 mLastNonConfigurationInstances
能够得到上一次的值,使得 mViewModelStore
值不变
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) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
fragment 重建后其内部的 getViewModelStore()
方法返回的对象是相同的
FragmentManagerViewModel
管理着内部的 ViewModelStore
和 child 的 FragmentManagerViewModel
。因此保证 mNonConfig值不变即能确保fragment中的 getViewModelStore()
不变。
只要保证作用域(viewModelStore)相同,即可获取相同的 ViewModel
实例
ViewModelStoreOwner
代表着作用域,其内部唯一的方法返回 ViewModelStore
对象
只要保证相同作用域的 ViewModelStore
对象相同就能保证相同作用域获取到相同的 ViewModel
对象
重建时 ViewModelStore
对象不变
activity/fragment 依赖它,而 ViewModel
不依赖 activity/fragment。因此只要不让 ViewModel
持有 context 或 view 的引用,就不会造成内存泄漏
activity 重建后 mViewModelStore 通过 ActivityThread 的一系列方法能够保持不变,从而当 activity 重建时 ViewModel 中的数据不受影响
通过宿主 activity 范围内共享的 FragmentManagerViewModel 来存储 fragment 的 ViewModelStore 和子 fragment 的 FragmentManagerViewModel ,而 activity 重建后 FragmentManagerViewModel 中的数据不受影响,因此 fragment 内部的 ViewModel 的数据也不受影响
通过同一 ViewModelStoreOwner 获取的 ViewModelStore 相同,从而保证同一作用域通过 ViewModelProvider 获取的 ViewModel 对象是相同的
的 FragmentManagerViewModel ,而 activity 重建后 FragmentManagerViewModel 中的数据不受影响,因此 fragment 内部的 ViewModel 的数据也不受影响
通过同一 ViewModelStoreOwner 获取的 ViewModelStore 相同,从而保证同一作用域通过 ViewModelProvider 获取的 ViewModel 对象是相同的
通过单向依赖(视图控制器持有 ViewModel )来解决内存泄漏的问题
参考自:【背上Jetpack之ViewModel】即使您不使用MVVM也要了解ViewModel ——ViewModel 的职能边界
https://juejin.im/post/6844904100493017095