搞懂Android Jetpack ViewModel 使用及原理
ViewModel 的官方解释
ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存
ViewModel 的用处
- 屏幕切换后,数据不会丢失
根据官方简介,当界面发生横竖屏切换时,viewmodel
中的数据不会发生变化。只有当Activity/Fragment
执行finish
被销毁时,ViewModel
才会被销毁。ViewModel
的生命结束周期是与Activity/Fragment
一致的,所以不存在内存泄漏的问题。ViewModel
对应的生命周期图如下所示,当屏幕旋转时,并不会执行onCleard
回调,只有当调用finish()
时,才会执行。
public class AppCompatActivity extends FragmentActivity
……
public class FragmentActivity extends SupportActivity implements ViewModelStoreOwner,
由上面代码可知,AppCompatActivity
实现了ViewModelStoreOwner
,自身有一个ViewModelStore对象mViewModelStore
,当getViewModelStore()
回调时,进行以下操作。
首先判断如果为null,则从上次屏幕切换时缓存的数据中获取,如果缓存中为null,则创建一个新的。
FragmentActivity.java
@NonNull
public ViewModelStore getViewModelStore() {
if (this.getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the Application instance. You can't request ViewModel before onCreate call.");
} else {
if (this.mViewModelStore == null) {
FragmentActivity.NonConfigurationInstances nc = (FragmentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance();
if (nc != null) {
this.mViewModelStore = nc.viewModelStore;
}
if (this.mViewModelStore == null) {
this.mViewModelStore = new ViewModelStore();
}
}
return this.mViewModelStore;
}
}
- 在 Fragment 之间共享数据
Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的情况。想象一下主从 Fragment 的常见情况,假设您有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。这种情况不太容易处理,因为这两个 Fragment 都需要定义某种接口描述,并且所有者 Activity 必须将两者绑定在一起。此外,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。
可以使用 ViewModel 对象解决这一常见的难点。这两个 Fragment 可以使用其 Activity 范围共享 ViewModel 来处理此类通信
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 都会检索包含它们的 Activity。这样,当这两个 Fragment 各自获取 ViewModelProvider 时,它们会收到相同的 SharedViewModel 实例(其范围限定为该 Activity)。此方法具有以下优势:
1.Activity 不需要执行任何操作,也不需要对此通信有任何了解。
2.除了 SharedViewModel 约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。
3.每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。
- 担任MVVM中的VM角色
MVVM中的VM即是ViewModel,作为View和Model之间的连接桥梁,配合LiveData,可以轻松实现观察者模式。
ViewModel用法
创建ViewModel
类,继承AndroidViewModel
public class UserViewModel extends AndroidViewModel {
public MutableLiveData<String> notifyDateChanged = new MutableLiveData<>();
protected CompositeDisposable mCompositeDisposable = new CompositeDisposable();
@Override
protected void onCleared() {
super.onCleared();
if (mCompositeDisposable != null) {
mCompositeDisposable.clear();
}
}
public UserViewModel(@NonNull Application application) {
super(application);
}
}
发送数据
mUserVM.notifyDateChanged.setValue("hello");
Activity/Fragment
中初始化并添加监听,接收数据
mUserVM = ViewModelProviders.of(this).get(UserViewModel.class);
mUserVM.notifyDateChanged.observe(this, result -> {
//todo here
});
ViewModel 实例化源码分析
of(@NonNull FragmentActivity activity)
ViewModelStore
是一个存储ViewModel的容器,ViewModel 通过键值对存放其中。HashMap<String, ViewModel> mMap = new HashMap<>();
- 初始化
ViewModelProvider
(ViewModelProvider.java)
主要作用是对mFactory
,mViewModelStore
进行赋值
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
AndroidViewModelFactory
是用来实例化ViewModel的工厂类,继承自Factory
,此处AndroidViewModelFactory进行初始化。后面在获取ViewModel实例时,会调用到AndroidViewModelFactory.create(@NonNull Class<T> modelClass)
来创建新的ViewModel
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
- ViewModelStores.java,初始化
ViewMoldeStore
对象
如果当前版本的FragmentActivity
实现了ViewModelStoreOwner
接口,则可以直接调用getViewModelStore()
。FragmentActivity
会在getViewModelStore()
回调中,创建ViewModelStore
。
如果当前版本FragmentActivity
未实现ViewModelStoreOwner
接口,则会创建一个空的Fragment->HolderFragment
,来使ViewModelStore
绑定其生命周期。
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
return holderFragmentFor(activity).getViewModelStore();
}
FragmentActivity
中的getViewModelStore()
回调
@NonNull
public ViewModelStore getViewModelStore() {
if (this.getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the Application instance. You can't request ViewModel before onCreate call.");
} else {
if (this.mViewModelStore == null) {
FragmentActivity.NonConfigurationInstances nc = (FragmentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance();
if (nc != null) {
this.mViewModelStore = nc.viewModelStore;
}
if (this.mViewModelStore == null) {
this.mViewModelStore = new ViewModelStore();
}
}
return this.mViewModelStore;
}
}
从上面代码中可以推断出,在屏幕发生变化时,会保存ViewModelStore
,再次创建时,会优先从缓存中读取。如果没有,则创建一个新的ViewModelStore
。
3.创建HolderFragment
(以宿主为Activity为例)
HolderFragment holderFragmentFor(FragmentActivity activity) {
FragmentManager fm = activity.getSupportFragmentManager();
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
return holder;
}
holder = mNotCommittedActivityHolders.get(activity);
if (holder != null) {
return holder;
}
if (!mActivityCallbacksIsAdded) {
mActivityCallbacksIsAdded = true;
activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
}
holder = createHolderFragment(fm);
mNotCommittedActivityHolders.put(activity, holder);//以activity为键,保证同一个activity对应的holderFragment对象相同
return holder;
}
public HolderFragment() {
setRetainInstance(true);
}
@Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
ViewModelStore.java执行ViewModel.onCleared()
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
-
mNotCommittedActivityHolders
是类型为Map<Activity, HolderFragment>
,所以同一个宿主Activity
对应的HolderFragment
为同一个。同理,同一个宿主Fragment
的对应的HolderFragment
也为同一个。所以其ViewModelStore
对象为同一个。 - 如果
HolderFragment
不存在,则创建一个新的,将其保存到mNotCommittedActivityHolders
- 调用
Fragment#setRetaininstance(true)
允许我们跳过销毁和重新创建的周期。指示系统保留当前的fragment实例。这样,实现HolderFragment中的ViewModelStore不会随着屏幕转换而发生变化。 - HolderFragment 执行
onDestroy
时,调用ViewModelStore.clear()
,完成ViewModel的onClear()
的回调。
get(@NonNull Class modelClass)
ViewModelProvider
初始化之后,调用get(@NonNull Class<T> modelClass)
获取ViewModel实例
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);
}
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
由上面两部分代码可知,get(@NonNull Class<T> modelClass)
参数传入ViewModel.class后,以Class.getCanonicalName()
为键,从ViewModelStore
中取出相应ViewModel实例并返回。如果ViewModelStore
中不存在,调用mFactory.create(modelClass)
创建一个新的ViewModel对象,将其放到ViewModelStore
的容器中。
调用mFactory.create(modelClass)
会执行ViewModelProvider.AndroidViewModelFactory.create(@NonNull Class<T> modelClass)
,通过反射进行ViewModel
实例化。如果目标ViewModel
继承自AndroidViewModel
,则调用AndroidViewModel
的构造函数,如果是继承自ViewModel
,则调用ViewModel
无参构造函数
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
return super.create(modelClass);
}
至此ViewModel 实例化完成
END
推荐阅读
-
Android系统开发中log的使用方法及简单的原理
-
Android系统开发中log的使用方法及简单的原理
-
Android HandlerThread的使用及原理详解
-
Android线程—AsyncTask的使用及原理
-
Android - AsyncTask 使用及原理
-
深度:一文搞懂Ribbon使用及内核原理剖析
-
Android Jetpack架构组件(六)一文带你了解ViewModel的使用和原理
-
Android架构组件ViewModel和LiveData介绍及使用
-
Android Jetpack:ViewModel, LiveData & Databinding 的简单使用
-
Android Jetpack组件ViewModel基本使用和原理分析