Architecture Components之ViewModel源码分析

简介

ViewModel的作用如它的名字所示,用来存储View相关的数据,并且他会自动感知当前界面的生命周期,以便在界面重建的时候能够及时恢复数据(如屏幕旋转、语言等Configuration变化的时候)

一分钟上手

我们来简单看下他的使用方法,真的非常简单,首先实现一个自定义的ViewModel(先忽略LiveData,可把它理解为一个数据的包装器):

1
2
3
4
5
6
7
8
9
10
public class CustomViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<User>>();
loadUsers();
}
return users;
}
}

随后使用ViewModelProvider.get(Class<T>)方法即可获取到对应的ViewModel实例,结束,就是这么简单。当界面重建并再次调用此方法获取ViewModel的时候,将会拿到之前Activity创建的实例。

1
CustomViewModel model = ViewModelProviders.of(this).get(CustomViewModel.class);

RTFSC

是不是觉得很神奇,当界面重建的时候,数据被存到哪了呢?让我们一同翻翻它的源码来找答案~

ViewModel的生命周期

先来简单熟悉下主角ViewModel的生命周期,可以看到,在界面真正onDestroy()之前,ViewModel是会一直存活的。也就是说ViewModel的生命周期会比Activity的长,为了避免内存泄漏,我们千万不能随便持有Activity的引用。
viewmodel-lifecycle

ViewModel vs AndroidViewModel

首先看下ViewModelAndroidViewModel这两个类,实现都非常简单。其中onCleared()方法会在ViewModel不再使用并即将销毁的时候调用。AndroidViewModel继承ViewModel,比它多了一个Application成员变量。因此,当我们需要在ViewModel中使用Context的时候,就必须选用AndroidViewModel,否则就很容易出现内存泄漏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class ViewModel {
protected void onCleared() {
}
}

public class AndroidViewModel extends ViewModel {
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
public <T extends Application> T getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}

ViewModelProvider

回到生成ViewModel实例的那段代码ViewModelProviders.of(this).get(CustomViewModel.class),我们是通过ViewModelProvidersof(FragmentActivity)方法来获取ViewModelProvider

1
2
3
4
5
6
7
8
9
10
11
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
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);
}

当我们没有提供Factory的时候,默认会使用AndroidViewModelFactory来生成ViewModel对象,其中Factory是一个工厂方法接口,用于产生对应类型的ViewModel对象。

1
2
3
public interface Factory {
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

可以看到AndroidViewModelFactory的实现十分简单粗暴,如果class是AndroidViewModel类型的,就直接调用带Application的构造器new对象,否则调用默认构造器new对象。若我们的ViewModel需要更多的参数才能创建,在上一步的of(FragmentActivity)方法中就必须传入自定义的Factory类了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static class AndroidViewModelFactory extends NewInstanceFactory {
private Application mApplication;
public AndroidViewModelFactory(@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 (XXXException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
return super.create(modelClass);
}
}

public static class NewInstanceFactory implements Factory {
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.newInstance();
} catch (XXXException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
}

成功创建了ViewModelProvider后,我们就可以通过get(Class<T>)方法来获取一个ViewModel了。从实现来看也非常简单,通过key从ViewModelStore中拿出对象,如果不存在则创建一个新的丢进去并返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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;
}

ViewModelStore

接下来看ViewModelStore的实现,从类名来看就是一个存储ViewModel的地方,实现也还真的非常简单,采用了HashMap来存储对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
}
...
}

我们回去看看ViewModelStore到底是怎么被创建的,是通过ViewModelStores.of(FragmentActivity)方法创建的。我们来瞧一瞧它的实现代码:

1
2
3
4
5
6
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
return holderFragmentFor(activity).getViewModelStore();
}

这里需要区分下Support Library的版本,如果在v27.1.0版本及以上的,FragmentActivity已经实现了ViewModelStoreOwner接口,所以返回的是activity.getViewModelStore()

1
2
3
4
5
6
7
8
9
10
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.");
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
return mViewModelStore;
}

那么界面在重建的时候是如何保存这个ViewModelStore的呢?关键就在onRetainNonConfigurationInstance()这个方法,系统会自动将其存起来,然后onCreate()的时候再将其取出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public final Object onRetainNonConfigurationInstance() {
if (mStopped) {
doReallyStop(true);
}
Object custom = onRetainCustomNonConfigurationInstance();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
if (fragments == null && mViewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = mViewModelStore;
nci.fragments = fragments;
return nci;
}

protected void onCreate(@Nullable Bundle savedInstanceState) {
mFragments.attachHost(null /*parent*/);
super.onCreate(savedInstanceState);
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
...
}

如果在v27.1.0以下的版本,则返回的是holderFragmentFor(activity).getViewModelStore()。首先看下holderFragmentFor()逻辑,通过FragmentManager来获取HolderFragment,如果没有就通过FragmentManager添加并存储进mNotCommittedActivityHolders中。为什么叫NotCommitted呢?因为在commit之后不会马上添加完成,这是一个异步的过程,所以先把他存在mNotCommittedActivityHolders中,以免快速再次调用holderFragmentFor()方法的时候不能及时拿到commit的HolderFragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;
}

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

最后一步就是分析HolderFragment,到底他做了什么可以在重建的时候保存数据呢?关键就在于setRetainInstance(true)这个方法,它可以在Activity重建的时候继续存活,以达到保存数据的目的。

1
2
3
4
5
6
7
8
9
10
11
public class HolderFragment extends Fragment implements ViewModelStoreOwner {
...
private ViewModelStore mViewModelStore = new ViewModelStore();
public HolderFragment() {
setRetainInstance(true);
}
public ViewModelStore getViewModelStore() {
return mViewModelStore;
}
...
}

总结

至此,ViewModel如何在Activity重建的时候成功保存数据就分析完毕了。这里还得注意Support Library的版本问题,如果在v27.1.0及以上版本,是通过FragmentActivity.onRetainNonConfigurationInstance()方法实现的,而v27.1.0以下的版本则是通过Fragment.setRetainInstance(true)这个方法实现的,大家需要注意区别。还是觉得onRetainNonConfigurationInstance()的实现会优雅一点~