Android架构组件-ViewModel
ViewModel
ViewModel,从字面上理解的话,我们也能想到它肯定是跟视图(View)以及数据(Model)相关的。正像它字面意思一样,它是负责准备和管理和UI组件(Fragment/Activity)相关的数据类,也就是说ViewModel是用来管理UI相关的数据的。因此可以在配置变化时存活下来,例如当屏幕旋转的时候。
为什么需要ViewModel
app组件,例如Activity和Fragment拥有可以被安卓框架所管理的生命周期。框架可以决定销毁或创建它们,这是基于一些用户的行为或者设备的事件,而这一切都在你的控制范围外。
因为一些对象可能会被操作系统销毁或重新创建,任何你所持有的数据都会丢失。例如,你的Activity拥有一组用户列表,当Activity由于配置变化而重新创建时,新的Activity不得不重新获取用户列表。对于简单的数据,Activity可以使用onSaveInstanceState()方法并从onCreate()的bundle里恢复数据,但是这种方法仅仅适合于少量数据,例如UI状态,不适用于大量数据,例如一组用户列表。
另一个问题是,这些UI控制器(Activity、Fragment等)需要频繁地异步调用,可能会需要一些时间返回。UI控制器需要管理这些调用,并当被销毁的时候清除它们,以避免潜在的内存泄漏。这需要很多的代码维护,并且在由于配置变化而重新创建的情况下是很浪费资源的,因为需要重新进行相同的调用。
另外,这些UI控制器已经响应用户行为或处理操作系统交互。当它们也需要手动处理资源的时候,会使得类急速膨胀。上帝的Activity或上帝的Fragment,是指一个试图处理所有app工作的单独类,而不是分派到其他类去完成。这样也会使得测试工作很难进行。
将我们的UI和控制逻辑分离变得越来越容易和高效了。Lifecycle提供了一个新类叫做ViewModel,一个负责为UI准备数据的帮助类。ViewModel会在配置发生变化的时候自动保存,所以其所持有的数据会在新的Activity或新的Fragment立即可用。在上面我们所提及的例子中,应当是ViewModel的职责来获取并保持用户列表,而不是Activity或Fragment。
ViewModel基本使用
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<Users>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// 异步调用获取用户列表
}
}
现在新的Activity如下:
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// 更新 UI
});
}
}
如果Activity被重新创建了,它会收到被之前Activity创建的相同MyViewModel实例。当所属Activity终止后,框架调用ViewModel的onCleared()方法清除资源。
因为ViewModel在指定的Activity或Fragment实例外存活,它应该永远不能引用一个View,或持有任何包含Activity context引用的类。如果ViewModel需要Application的context(如获取系统服务),可以扩展AndroidViewmodel,并拥有一个构造器接收Application。
在Fragment间共享数据
一个Activity中的多个Fragment相互通讯是很常见的。每个Fragment需要定义接口描述,所属Activity将二者捆绑在一起。此外,每个Fragment必须处理其他Fragment未创建或不可见的情况
通过使用ViewModel可以解决这个痛点。想象一种情况,一个Fragment从列表项中选择一项,在另一个Fragment显示被选中项的内容。
这些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 onActivityCreated() {
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends LifecycleFragment {
public void onActivityCreated() {
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// update UI
});
}
}
请注意,两个Fragment都使用了getActivity(),以通过ViewModelProviders获取SharedViewModel。
这种方式的好处包括:
- Activity不需要做任何事情,也不需要知道通讯的事情
- Fragment不需要知道彼此,除了SharedViewModel进行联系。如果它们(Fragment)其中一个消失了,其余的仍然能够像往常一样工作
- 每个Fragment有自己的生命周期,而且不会受其它Fragment生命周期的影响。事实上,一个Fragment替换另一个Fragment,UI的工作也不会受到任何影响。
ViewModel的生命周期
ViewModel对象的范围由获取ViewModel时传递至ViewModelProvider的Lifecycle所决定。ViewModel始终处在内存中,直到Lifecycle永久地离开—对于Activity来说,是当它终止(finish)的时候,对于Fragment来说,是当它分离(detached)的时候。
ViewModel生命周期类图
查看源码,我们可以得出ViewModel相关的类图:
ViewModel相关类图
-
ViewModelProviders是ViewModel工具类,该类提供了通过Fragment和Activity得到ViewModel的方法,而具体实现又是有ViewModelProvider实现的。ViewModelProvider是实现ViewModel创建、获取的工具类。在ViewModelProvider中定义了一个创建ViewModel的接口类——Factory。ViewModelProvider中有个ViewModelStore对象,用于存储ViewModel对象。
-
ViewModelStore是存储ViewModel的类,具体实现是通过HashMap来保存ViewModle对象。
-
ViewModel是个抽象类,里面只定义了一个onCleared()方法,该方法在ViewModel不在被使用时调用。ViewModel有一个子类AndroidViewModel,这个类是便于要在ViewModel中使用Context对象,因为我们前面提到是不能在ViewModel中持有Activity的引用。
-
ViewModelStores是ViewModelStore的工厂方法类,它会关联HolderFragment,HolderFragment有个嵌套类——HolderFragmentManager。
时序图
在使用ViewModel的例子中,也许你会察觉得到一个ViewModel对象需要的步骤有点多啊,怎么不直接new一个出来呢?在你看到ViewModel的类图关系后,你应该就能明白了,因为是会缓存ViewModel对象的。下面以在Fragment中得到ViewModel对象为例看下整个过程的时序图。
ViewModel时序图
时序图看起来比较复杂,但是它只描述了两个过程:
- 得到ViewModel对象。
- HolderFragment被销毁时,ViewModel收到onCleared()通知。
源码分析
根据上面时序图中的内容,我们从两个方面着手研究下ViewModel相关源码。
获取ViewModel对象
得到ViweModel对象,首先是通过ViewModelProviders.of()方法得到ViewModelProvider对象,然后调用ViewModelProvider.get()方法得到ViewModel对象。
一、得到ViewModelProvider对象
直接看ViewModelProviders类的具体实现
public class ViewModelProviders {
@Deprecated
public ViewModelProviders() {
}
private static Application checkApplication(Activity activity) {
Application application = activity.getApplication();
if (application == null) {
throw new IllegalStateException("Your activity/fragment is not yet attached to "
+ "Application. You can't request ViewModel before onCreate call.");
}
return application;
}
private static Activity checkActivity(Fragment fragment) {
Activity activity = fragment.getActivity();
if (activity == null) {
throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
}
return activity;
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
return of(fragment, null);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
@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);
}
@SuppressWarnings("WeakerAccess")
@Deprecated
public static class DefaultFactory extends ViewModelProvider.AndroidViewModelFactory {
@Deprecated
public DefaultFactory(@NonNull Application application) {
super(application);
}
}
}
ViewModelProviders提供了四个of()方法,四个方法功能类似,其中of(FragmentActivity activity, Factory factory)和of(Fragment fragment, Factory factory)提供了自定义创建ViewModel的方法。
我们在时序图中使用of(Fragment fragment)最为研究对象,这里我们也从该方法开始研究,在该方法中主要有两个步骤。
1,判断是否需要初始化默认的Factory
这一过程很简单:通过调用initializeFactoryIfNeeded()方法判断是否需要初始化mDefaultFactory变量。在此之前又会判断Fragment的是否Attached to Activity,Activity的Application对象是否为空。
2,创建一个ViewModelProvider对象。
创建ViewModel对象看似很简单,一行代码搞定new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory)。但是里面的里面的逻辑比较深,我们慢慢把它抽丝剥茧,看看有多深。
先看看ViewModelStores.of()方法
@MainThread
public static ViewModelStore of(@NonNull Fragment fragment) {
return holderFragmentFor(fragment).getViewModelStore();
}
又是一行代码,holderFragmentFor()是HolderFragment的静态方法,HolderFragment继承自Fragment。我们先看holderFragment()方法的具体实现
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(Fragment fragment) {
return sHolderFragmentManager.holderFragmentFor(fragment);
}
public ViewModelStore getViewModelStore() {
return mViewModelStore; //返回ViewModelStore对象
}
holderFragmentFor()又是一行代码,继续看HolderFragmentManager.holderFragmentFor()方法的具体实现
HolderFragment holderFragmentFor(Fragment parentFragment) {
FragmentManager fm = parentFragment.getChildFragmentManager();
HolderFragment holder = findHolderFragment(fm); // 在activity和back stack中查找parentFragment
if (holder != null) {
return holder;
}
holder = mNotCommittedFragmentHolders.get(parentFragment);
if (holder != null) {
return holder;
}
// 注册Fragment生命周期回调
parentFragment.getFragmentManager()
.registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
holder = createHolderFragment(fm); //创建HolderFragment对象。
mNotCommittedFragmentHolders.put(parentFragment, holder);
return holder;
}
private FragmentLifecycleCallbacks mParentDestroyedCallback =
new FragmentLifecycleCallbacks() {
@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment parentFragment) {
super.onFragmentDestroyed(fm, parentFragment);
HolderFragment fragment = mNotCommittedFragmentHolders.remove(
parentFragment);
if (fragment != null) {
Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment);
}
}
};
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
HolderFragment holder = new HolderFragment(); // 创建HolderFragment对象
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}
public HolderFragment() {
//这个是关键,这就使得Activity被recreate时,Fragment的onDestroy()和onCreate()不会被调用
setRetainInstance(true);
}
在createHolderFragment()方法中,你也许发现调用FragmentTransaction.add()方法并没有传一个定义在layout文件中的id进去,而且你去看HolderFragment并没有重写Fragment.onCreateView()方法。这个有点反常,这是为什么呢? 这个疑惑可以从HolderFragment()构造方法中得到解答。在构造方法中调用了setRetainInstance(true)方法,该方法会使得HolderFragment所在的Activity被recreate时,HolderFragment不会被recreate,自然它就不会走onDestroy()、onCreate()方法。为什么不让HolderFragment被recreate呢?可以从ViewModel生命周期看出,ViewModel的生命周期比使用它的Activity/Fragment的生命周期更长。
到此为止,我们已经得到了ViewStore对象,前面我们在创建ViewModelProvider对象是通过这行代码实现的new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory)现在再看下ViewModelProvider的构造方法
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store; // 这个ViewStore对象实际上是HolderFragment中的ViewStore对象。
}
二、得到ViewModel对象
在上面我们已经得到了ViewModelProvider对象,现在就可以通过ViewModelProvider.get()方法得到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);
}
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key); //从缓存中查找是否有已有ViewModel对象。
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass); //创建ViewModel对象,然后缓存起来。
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
ViewModelProvider.get()方法比较简单,该说的都在注释中写明了。最后我们看下ViewModelStore类的具体实现
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.get(key);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
mMap.put(key, viewModel);
}
final ViewModel get(String key) {
return mMap.get(key);
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
ViewModelStore是缓存ViewModel的类,put()、get()方法用于存取ViewModel对象,另外提供了clear()方法用于清空缓存的ViewModel对象,在该方法中会调用ViewModel.onCleared()方法通知ViewModel对象不再被使用。
ViewModel收到onCleared()通知。
在前面我们提到,在HolderFragment的构造方法中调用了setRetainInstance(true);目的是为了在其所在的Activity被recreate时,HolderFragment不会被recreate,为什么要这么做呢(之前面卖的关子),那就要看下它的onDestroy()方法了。
@Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
在onDestroy()方法中调用了ViewModelStore.clear()方法,我们知道在该方法中会调用ViewModel的onCleared()方法。在你看了HolderFragment源码后,或许你会有个疑问,mViewModelStore保存的ViewModel对象是在哪里添加的呢? 细心的话,你会发现在ViewModelProvider的构造方法中,已经将HolderFragment中的ViwModelStore对象mViewModelStore的引用传递给了ViewModelProvider中的mViewModelStore,而在ViewModelProvider.get()方法中会向mViewModelStore添加ViewModel对象。
参考: