一.ViewModel介绍以及优势
二. ViewModel的使用
三. ViewModel的实现原理
四. 关于ViewModel相关的问题Q-And-A
ViewModel官方介绍
ViewModel 类的主要优势实际上有两个方面:
实现一个点击按钮让数字 + 1的功能
public class MyViewModel extends ViewModel {
public int number;
}
public class MainActivity extends AppCompatActivity {
private TextView textView;
private MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
textView.setText(String.valueOf(viewModel.number));
Log.d("jian", "onCreate: " + this + " " + viewModel);
}
public void plusNumber(View view) {
textView.setText(String.valueOf(++viewModel.number));
}
}
当旋转屏幕时,数据依旧不会丢失,也就是“新创建的activity实例状态跟销毁前状态一样”
从打印的信息也可知,虽然Activity对象被重建了(跟屏幕旋转前实例不一样),但ViewModel依然没有被销毁重建(跟屏幕旋转前实例一样)
学习ViewModel的实现原理前需要先掌握 Activity的保存实例状态机制,不懂的可以先阅读一下详解Activity和Fragment生命周期以及保存实例状态机制
ViewModel 的替代方案是保存要在界面中显示的数据的普通类。在 activity 或 Navigation 目的地之间导航时,这可能会造成问题。此时,如果您不利用保存实例状态机制存储相应数据,系统便会销毁相应数据。
public final Object onRetainNonConfigurationInstance() {
// Maintain backward compatibility.
Object custom = onRetainCustomNonConfigurationInstance();
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;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
//custom是我们重写onRetainCustomNonConfigurationInstance()后的返回值,
//也就是我们想要保存的“上个状态” (数据)
nci.viewModelStore = viewModelStore;
//这里将viewModelStore对象保存
return nci;
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
点进get看逻辑:
@NonNull
@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");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
在此处先获取class对象的CanonicalName,然后以 DEFAULT_KEY + “:” + canonicalName为键值key调用下面这个方法
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
//此处调用get(key)获取到viewmodel对象
if (modelClass.isInstance(viewModel)) {
//判断viewModel是不是这个class对象的实例,因为key值可以人为定义,可能造成同一键值获取到不同class的viewmodel实例,(因为我们使用的都是自定义Viewmodel(继承))
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
//onRequery默认空实现,不用去看
//OnRequeryFactory是要我们去继承和重写onRequery方法的,无需看
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
//抽象类,需要我们去继承和重写(不用看)
} else {
viewModel = mFactory.create(modelClass);
//利用传进来的viewmodelfactory去创建viewmodel对象
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
ViewModelFactory都是利用反射去创建viewmodel对象的(因为传进来的参数是class对象)
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
根据上面的代码可以知道,viewmodel是在mViewModelStore中利用key去获取的(猜测内部实现是hashmap)
再看一下ViewModelStore这个类(确实是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();
}
}
此时又在想,ViewModelStore对象是怎么来的? 接着继续跟踪mViewModelStore对象
private final ViewModelStore mViewModelStore;
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
@Override
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.");
}
ensureViewModelStore();
return mViewModelStore;
}
void ensureViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
void ensureViewModelStore() {
//当mViewModelStore不为null时,啥事不做
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
//获取上个activity配置状态改变时调用onRetainNonConfigurationInstance()的返回值,也就是保存下来的custom和mviewstore对象
if (nc != null) {
//把activity销毁前状态的viewModelStore赋值给重建后的activity的mviewModelStore
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
//owner对象必须传入getActivity()
MyViewModel viewModel = new ViewModelProvider(getActivity(), new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(MyViewModel.class);
写个小案例,利用livedata + viewmodel 实现fragment间数据的共享联动效果
拖动上面进度条时下面的进度条会跟着动,拖动下面的进度条时上面的进度条会跟着动
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_first, container, false);
SeekBar seekBar = root.findViewById(R.id.seekBar);
//只要上下俩个fragment的此处owner传入getActivity()就能实现了
MyViewModel viewModel = new ViewModelProvider(getActivity(), new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(MyViewModel.class);
Log.d("ning", "onCreateView: " + viewModel);
viewModel.getProgress().observe(getActivity(), new Observer<Integer>() {
@Override
public void onChanged(Integer i) {
seekBar.setProgress(i);
}
});
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
viewModel.getProgress().setValue(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// Inflate the layout for this fragment
return root;
}
ViewModel 存储在 FragmentManagerViewModel 中的,而 FragmentManagerViewModel 是存储在宿主 Activity 中的 ViewModelStore 中,又因 Activity 中 ViewModelStore不会因配置改变而销毁,故 Fragment 中 ViewModel 也不会因配置改变而销毁。
若要看源码请自行看,这个涉及到Activity的生命周期方法的详细逻辑
在ComponentActivity.java中可以看到
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
// Clear out the available context
mContextAwareHelper.clearAvailableContext();
// And clear the ViewModelStore
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
当 event == Lifecycle.Event.ON_DESTROY 和 isChangingConfigurations()为false 时,(即Activity会销毁且不是因为状态改变被销毁时)会调用 getViewModelStore().clear()方法把viewmodelstore给清空掉
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
因此在调用finish()方法(人为或者系统) 的时候 viewmodelstore数据是会被清空的,即Activity再创建的时候数据不会被保存。