您的位置:首页 > 运维架构 > 网站架构

Android架构组件三 Android Architecture Components ViewModel组件解析

2018-02-10 18:07 956 查看

1 前言

ViewModel是android架构组件中非常重要的一个组件,它是Android架构分层的核心,有关它的用法和资料可以参考

Android架构组件一 Android Architecture Components开发入门

也可以参考官方给出的示例https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html

接下来我们从以下几个方面介绍ViewModel组件

2 ViewModel的作用

ViewModel组件的官方定义如下:

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.

翻译过来就是ViewModel是存储和管理lifecycle 重新创建的数据的组件,在lifecycle 在配置改变或者屏幕旋转时数据仍然在生存周期内。另外ViewModel还可以用来负责UI组件间的通信,它是解耦Activity/Fragment View层的关键。

ViewModel在Android架构组件中的位置如下:



可以看到ViewModel处于Activity(View)和Repository(Model)层之间,它类似于MVP中的P层,向下对View层提供操作Model层的接口,向上使用观察者模式观察Model层,当Model层感兴趣的数据发生变化时通知View层,当然这个是ViewModel中引用的LiveData组件来实现的,这个将会在下面一篇博文中来讲解。

ViewModel组件有非常大的一个意义就是它的实例不会因为Activity/Fragment因为配置发生改变或者屏幕旋转时重新创建ViewModel对象。这个是ViewModel区别与MVP中P的基本特点之一。官方给出了如下的图示说明ViewModel的生命周期。



可以看到,只有当Activity的finish()方法被调用时,ViewModel.onCleared()方法会被调用,对象才会被销毁。这张图很好的描述了是当Activity被recreate时,ViewModel的生命周期。关于这一点是如何做到的,参考下面的讲解。

注意:*在ViewModel中不要持有Activity等View层的引用,如果需要引用Context,请使用AndroidViewModel,它引用了Application Context*

官方说明如下:

Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context

3 ViewModel UML类图

查看Android架构组件的源码,我们可以得到如下的UML类图。



这里有几个比较重要的类

ViewModelProviders:创建ViewModelProvider的工具类,提供以下四个方法创建ViewModelProvider

public static ViewModelProvider of(@NonNull Fragment fragment)
public static ViewModelProvider of(@NonNull FragmentActivity activity)
public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory)
public static ViewModelProvider of(@NonNull FragmentActivity activity,@NonNull Factory factory)


创建时可以指定创建的Factory ,默认已经有一个Factory,定义如下:

public static class DefaultFactory extends ViewModelProvider.NewInstanceFactory {

private Application mApplication;

/**
* Creates a {@code DefaultFactory}
*
* @param application an application to pass in {@link AndroidViewModel}
*/
public DefaultFactory(@NonNull Application application) {
mApplication = application;
}

@NonNull
@Override
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);
}
}


重写了public < T extends ViewModel > T create(@NonNull Class < T > modelClass)创建ViewModel对象。

ViewModelProvider: 真正创建和存储ViewModel的类,将创建的ViewModel保存在ViewModelStore 对象mViewModelStore中

创建逻辑如下:

@NonNull
@MainThread
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;
}


ViewModelStores:创建ViewModelStore的工具类,关联着HoldFragment ,并返回HolderFragment中的ViewModelStore对象

ViewModelStore: 保存和管理ViewModel的类,里面引用一个HashMap来保存ViewModel对象。并在HolderFragment销毁时清理掉所保存的ViewModel对象。

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();
}
}


HolderFragment:继承自Fragment,是ViewModel在配置改变不被销毁的秘密所在,它主要为ViewModelProvider创建ViewModel提供所需要的ViewModelStore。

在ViewModelStores中有如下关键代码:

public static ViewModelStore of(@NonNull FragmentActivity activity) {
return holderFragmentFor(activity).getViewModelStore();
}


这里的holderFragmentFor就是HolderFragment中的方法,定义如下:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
return sHolderFragmentManager.holderFragmentFor(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);
return holder;
}


有关这些类的详细实现请参考源码。

4 ViewModel解析

1 ViewModel实例的创建过程

ViewModel实例的创建过程比较复杂,先经过ViewModelProviders类,再经过ViewModelStores,在跳转到HolderFragment得到ViewModelStore对象,最终跳转到ViewModelProvider的public < T extends ViewModel > T get(@NonNull String key, @NonNull Class< T> modelClass)方法中创建的。整个流程如下:



因此,这里我们最终来看ViewModelProvider的public < T extends ViewModel > T get(@NonNull String key, @NonNull Class< T> modelClass)方法

@NonNull
@MainThread
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;
}


主要做了以下几件事情

(1) 从mViewModelStore中获取ViewModel对象,mViewModelStore是一个ViewModelStore对象,在创建ViewModelProvider传递过来的,第一次肯定获取为NULL。

(2) 判断是否是ViewModel的实例对象,如果是直接返回,当获取的不为NULL时,且传递的Class对象正确,就就可以在这里直接返回了,说明VikewModel对象之前创立过

(3) 调用mFactory的< T extends ViewModel > T create(@NonNull Class< T> modelClass)创建一个ViewModel对象,mFactory由创建ViewModelProvider指定,一般通过反射创建该对象。例如默认的DefaultFactory对象。

(4) 创建好ViewModel对象后将该对象缓存起来,保存到mViewModelStore中并返回ViewModel对象。

这样,整个ViewModel创建的过程就结束了。还是借用一下别人的序列图。



2 ViewModel实例为什么不会因为Activity 重新创建而销毁?

这个问题很有意思,从一开始接触到ViewModel官方的说明我就很好奇,这个是如何实现的呢?我们知道,在Activity/Fragment 因为屏幕旋转,配置改变时,里面所引用的对象都会被重建,典型的是对话框,为此我们不得不在Activity的onSaveInstanceState()保存一些数据,然后在onCreate()中恢复数据。然而ViewModel却不需要,那么ViewModel是如何做到的呢?我们查看ViewModelProviders的源码发现每次的of()方法又都是创建一个新的ViewModelProvider对象的,这就更加疑惑了。

@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
initializeFactoryIfNeeded(checkApplication(activity));
return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory);
}


答案是有两个关键点:

(1) HolderFragment在Activity/Fragment 被 re-create 的生命周期

(2) ViewModelProvider 中 mViewModelStore对象

这里分别依次来说明这两个关键点

(1) HolderFragment在Activity/Fragment 被 re-create 的生命周期

仔细阅读HolderFragment的源码,我们发现在构造方法中有这么一句

public HolderFragment() {
setRetainInstance(true);
}


很不起眼,却非常重要。

setRetainInstance(true);表明Fragment 不会因为Activity/Fragment 因为配置改变,屏幕旋转被销毁时,他会驻留在系统中,知道Activity/Fragment 被重新创建,然后onAttach上继续使用,也就是说HolderFragment在Activity/Fragment 因为配置改变,屏幕旋转被销毁时,不会走onDestory() –> onCreate()方法。这样保证了Activity/Fragment重新创建时还是之前那个HolderFragment对象,当然了里面的mViewModelStore也不会变。

(2) ViewModelProvider 中 mViewModelStore对象

由前面的分析得到Activity/Fragment重新创建时HolderFragment不会被重新创建,那么自然ViewModelStores中返回的ViewModelStore还是之前的ViewModelStore

@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
return holderFragmentFor(activity).getViewModelStore();
}


这样子导致在创建ViewModelProvider传进去的ViewModelStore 对象还是Activity/Fragment之前的ViewModelStore 对象

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}


这样即使new 再多的ViewModelProvider实例,它里面的mViewModelStore 对象并没有被重新创建。这样在调用get(@NonNull String key, @NonNull Class < T> modelClass)方法时,将会在步骤(2)中返回,返回的ViewModel对象就是Activity/Fragment重新创建之前的那个ViewModel对象。

通过(1)(2)就保证了ViewModel实例不会因为Activity 重新创建而销毁。

3 HoldFragment解析

在ViewModel组件中定义了一个HoldFragment类,该类继承Fragment,但是里面没有任何布局文件,它最主要的作用就是来生成在Activity/Fragment重新创建时保证返回的ViewModelStore对象不会改变。为此它的实现如下:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class HolderFragment extends Fragment {
private static final String LOG_TAG = "ViewModelStores";

private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();

/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final String HOLDER_TAG =
"android.arch.lifecycle.state.StateProviderHolderFragment";

private ViewModelStore mViewModelStore = new ViewModelStore();

public HolderFragment() {
setRetainInstance(true);
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sHolderFragmentManager.holderFragmentCreated(this);
}

@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}

@Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}

public ViewModelStore getViewModelStore() {
return mViewModelStore;
}

/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
return sHolderFragmentManager.holderFragmentFor(activity);
}

/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(Fragment fragment) {
return sHolderFragmentManager.holderFragmentFor(fragment);
}

@SuppressWarnings("WeakerAccess")
static class HolderFragmentManager {
private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>();

private ActivityLifecycleCallbacks mActivityCallbacks =
new EmptyActivityLifecycleCallbacks() {
@Override
public void onActivityDestroyed(Activity activity) {
HolderFragment fragment = mNotCommittedActivityHolders.remove(activity);
if (fragment != null) {
Log.e(LOG_TAG, "Failed to save a ViewModel for " + activity);
}
}
};

private boolean mActivityCallbacksIsAdded = false;

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);
}
}
};

void holderFragmentCreated(Fragment holderFragment) {
Fragment parentFragment = holderFragment.getParentFragment();
if (parentFragment != null) {
mNotCommittedFragmentHolders.remove(parentFragment);
parentFragment.getFragmentManager().unregisterFragmentLifecycleCallbacks(
mParentDestroyedCallback);
} else {
mNotCommittedActivityHolders.remove(holderFragment.getActivity());
}
}

private static HolderFragment findHolderFragment(FragmentManager manager) {
if (manager.isDestroyed()) {
throw new IllegalStateException("Can't access ViewModels from onDestroy");
}

Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
if (fragmentByTag != null && !(fragmentByTag instanceof HolderFragment)) {
throw new IllegalStateException("Unexpected "
+ "fragment instance was returned by HOLDER_TAG");
}
return (HolderFragment) fragmentByTag;
}

private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
HolderFragment holder = new HolderFragment();
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}

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);
return holder;
}

HolderFragment holderFragmentFor(Fragment parentFragment) {
FragmentManager fm = parentFragment.getChildFragmentManager();
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
return holder;
}
holder = mNotCommittedFragmentHolders.get(parentFragment);
if (holder != null) {
return holder;
}

parentFragment.getFragmentManager()
.registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
holder = createHolderFragment(fm);
mNotCommittedFragmentHolders.put(parentFragment, holder);
return holder;
}
}
}


首先在构造方法中调用了setRetainInstance(true),这样子在Activity/Fragment 因为配置发生改变时重新创建时,就不会销毁该HolderFragment 的实例,这样子HolderFragment 在下次Activity/Fragment重新创建时,并不会回调onDestory(),onCreate()方法,关于这一点。可以打点确认。

HoldFragment将创建实例的方法交给了HolderFragmentManager这个内部类,这个内部类首先创建了两个Map来保存了还未Attach但将要Attach 到Activity/Fragment的HoldFragment实例,并且注册了Activity/Fragment 的生命周期回调。接着创建HolderFragment实例,然后添加到map中去。

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);
return holder;
}


最后在HolderFragment的onCreate() 移除Map中添加的HoldFragment实例。因为回调到onCreate()方法,就说明HoldFragment已经成功的Attach到相应的Activity/Fragment 上了,这个时候需要移除mNotCommittedActivityHolders中的HoldFragment实例。

最后,在Activity finish之后,HoldFragment 会调用onDestory()方法,在这里面会清理掉mViewModelStore 中的ViewModel。

至此,ViewModel组件的分析已经基本完毕,下一篇将分析Android架构组件中LiveData组件

参考:

https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html

https://shymanzhu.com/2017/12/28/Android%E6%9E%B6%E6%9E%84%E7%BB%84%E4%BB%B6%EF%BC%88%E4%B8%89%EF%BC%89%E2%80%94%E2%80%94ViewModel/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐