您的位置:首页 > 移动开发 > Android开发

Architecture Components源码分析之ViewModel

2017-12-26 10:28 363 查看
如果还不清楚什么是ViewModel,可以看下[译] Architecture Components 之 ViewModel 这个系列的文章,翻译自Android Developer的官方文章。

ViewModel 类是被设计用来存储和管理 UI 相关的数据,主要实现了两个功能:

1. 在配置更改(如:屏幕旋转)时数据可以保留下来。

2. 在 Fragment 之间共享数据。

接下来会通过分析源码的方式来看看是如何实现这两个功能的。

我们先来找到ViewModel这个类

ViewModel

public abstract class ViewModel {
/**
* This method will be called when this ViewModel is no longer used and will be destroyed.
* <p>
* It is useful when ViewModel observes some data and you need to clear this subscription to
* prevent a leak of this ViewModel.
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}


发现只是一个抽象类,并且只有一个空实现的方法,说明实现特殊功能的代码一定在其他地方。

看过官方介绍的应该知道,ViewModel是通过ViewModelProvider创建的:

public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// 更新 UI
});
}
}


那我们从这里开始分析,通过调用
ViewModelProviders.of(this).get(MyViewModel.class)
是如何获取到ViewModel的那?我们来看
ViewModelProviders
of()
方法(of方法重载还有Fragment,这里我们只分析Activity,Fragment与activity如出一辙):

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

@SuppressLint("StaticFieldLeak")
private static DefaultFactory sDefaultFactory;

private static void initializeFactoryIfNeeded(Application application) {
if (sDefaultFactory == null) {
sDefaultFactory = new DefaultFactory(application);
}
}

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


ViewModelProviders.of()
方法返回了一个ViewModelProvider对象,该对象需要两个参数:ViewModelStore、Factory。通过命名,我们可以猜测ViewModelStore是一个ViewModel的仓库,用于缓存ViewModel,Factory是工厂类,用于创建ViewModel实例。获取到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);
}

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


ViewModelProvider类中将构造的参数ViewModelStore和Factory作为成员变量,
get
方法先是从
mViewModelStore.get
中获取,如果没有获取到则通过
Factory
创建一个
ViewModel
实例,并放入
ViewModelStore
中,这种使用方式更加验证了上面我们的猜测,之后会仔细分析
ViewModelStore
以及
Factory


既然有一个
ViewModel
的缓存
ViewModelStore
,那第一个功能:在配置更改(如:屏幕旋转)时数据可以保留下来,就很好理解了。只要让缓存在Activity配置更改重建是存活下来,那重建后获取的
ViewModel
就是之前缓存的那个了。

接下的问题便是:
ViewModelStore
存放在哪里可以保证在Activity配置更改重建是存活下来?

ViewModelStore

字面意思即为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();
}
}


这个很简单,也很好理解,仅仅是一个HashMap用于存放ViewModel,提供放入,获取,清空的方法。

我们回到
ViewModelProviders.of()
方法来,这里是通过
ViewModelStores.of(activity)
获取到的ViewModelStore对象的,我们继续进入这个方法:

import static android.arch.lifecycle.HolderFragment.holderFragmentFor;

public class ViewModelStores {

private ViewModelStores() {
}

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


注意到通过静态引入的方法调用了
HolderFragment
holderFragmentFor ()
方法,接着找到
HolderFragment


HolderFragment

public class HolderFragment extends Fragment {

...//去除了framgent相关的代码,只保留activity相关

private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();

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

public static HolderFragment holderFragmentFor(FragmentActivity activity) {
return sHolderFragmentManager.holderFragmentFor(activity);
}

static class HolderFragmentManager {
private Map<Activity, HolderFragment> mNotCommittedActivityHolders = 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;

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


这个类就是ViewModel的核心类了,所有的功能都是通过该类来实现的,注意看好啦~

HolderFragment.holderFragment()
方法直接返回了
sHolderFragmentManager.holderFragmentFor(activity)
的结果。而
HolderFragmentManager
holderFragmentFor
方法实际上就是创建了一个HolderFragment的实例,并添加到参数activity中,为了避免重复添加,先是调用
findHolderFragment(fm)
看看能否找到已添加HolderFragment,如果没有的话再从缓存的Map中找,还是没有话才去创建一个新的实例,放入缓存的Map,并返回这个对象,继而调用
getViewModelStore()
获取
viewModelStore
实例。

我们找到了
ViewModelStore
存放位置,是在HolderFragment中,那它如何保证在Activity配置更改重建是存活下来的?其实关键代码就是Fragment的一个方法:

/**
* Control whether a fragment instance is retained across Activity
* re-creation (such as from a configuration change).  This can only
* be used with fragments not in the back stack.  If set, the fragment
* lifecycle will be slightly different when an activity is recreated:
* <ul>
* <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
* will be, because the fragment is being detached from its current activity).
* <li> {@link #onCreate(Bundle)} will not be called since the fragment
* is not being re-created.
* <li> {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} <b>will</b>
* still be called.
* </ul>
*/
setRetainInstance(true);


就是这个方法保证了activity因配置更改重建时,该fragment的实例不会销毁,重建后的Activity还是使用该实例。

创建HolderFragment 的过程还有很多细节。

需要注意的是
HolderFragmentManager
是声明在
HolderFragment
中的static成员,因此会随着
HolderFragment
的首次加载创建实例,只存在一个实例并永远在内存中,缓存的map是
HolderFragmentManager
的成员变量,也会永远在内存中,而
HolderFragment
可以创建多个实例,所以对于不再需要的
HolderFragment
实例,需要及时从map中移除。

if (!mActivityCallbacksIsAdded) {
mActivityCallbacksIsAdded = true;
activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
}


这段代码是通过Application的registerActivityLifecycleCallbacks注册一个全局Activity生命周期的回调,任何Activity触发了生命周期都会在mActivityCallbacks中回调对应的方法。
HolderFragment
的源码中就是通过该回调,在绑定
HolderFragment
的Activity触发onDestroy方法后移除map中的缓存。

一开始我以为
HolderFragmentManager
会缓存
HolderFragment
直到依附的activity销毁才会移除缓存,但后来注意到在
HolderFragment
onCreate
方法中调用了
sHolderFragmentManager.holderFragmentCreated(this);
直接移除了缓存。因此,这个缓存仅仅是从
HolderFragment
的add方法调用到
onCreate
方法执行为止。或者add了Fragment但是还没有添加到Activity执行onCreate方法,依附的Activity就销毁了,也会回调mActivityCallbacks的onDestroy方法移除HolderFragment的缓存。我想了很久也没有想到这个缓存的使用场景,好像这个缓存是没有意义的。

Factory

即为创建ViewModel的工厂类,是一个接口,我们可以实现这个接口定义自己的ViewModel工厂。

/**
* Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
*/
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
* <p>
*
* @param modelClass a {@code Class} whose instance is requested
* @param <T>        The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}


上面
of()
使用的
sDefaultFactory
默认工厂:

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 static class NewInstanceFactory implements Factory {

@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
}


DefaultFactory可以创建
AndroidViewModel
的对象,调用它
AndroidViewModel(@NonNull Application application)
构造创建实例,如果不是AndroidViewModel.class则调用父类NewInstanceFactory的create方法调用ViewModel无参数的构造。

如果你的ViewModel实例的创建需要其他参数,则要自己实现Factory复写create。

总结

ViewModelProviders.of()
提供
ViewModelProvider
ViewModelProvider
通过
ViewModelStore
Factory
管理和创建ViewModel,
ViewModelStore
的引用存储在向目标Activity/Fragment中添加的无界面
HolderFragment
中,并通过
setRetainInstance(true);
以保证在Activity配置更改重建是存活下来。

关于第二条功能:在 Fragment 之间共享数据也很好理解了,在同一个Activity的不同Fragment种使用
ViewModelProviders.of()
时,参数需要传入Activity对象,第一次获取ViewModel时会创建一个新对象,而另一个Fragment获取相同ViewModel时,则会从
ViewModelStore
的缓存中获取,两个Fragment持有的时同一个ViewModel对象,就能实现Fragment之间通讯了。但是这种通讯的前途是必须在同一个Activity中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 源码 mvvm