官方文档:https://developer.android.com/topic/libraries/architecture/viewmodel
前言
ViewModel的源码比较简单,代码也不是很多,网上已经有了很多的文章来讲 ViewModel,但是最近查看源码时发现源码中状态保存的实现逻辑有了较大的变化,所以还是记录下来,并重新梳理了一遍源码的分析过程。
PS:本文是基于 android.arch.lifecycle:viewmodel:1.1.1 的源码进行分析
简单示例
class CountLiveData : LiveData() {
private var count = 0
fun doCount() {
value = ++count
}
}
class MyViewModel : ViewModel() {
var data = CountLiveData()
fun doCount() {
data.doCount()
}
}
class ViewModelDemoActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_demo_view_model)
// 获取 ViewModel 对象
val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
// 监听数据源
viewModel.data.observe(this, Observer { tv_test.text = it.toString() })
// 点击按钮改变数据
btn_test.setOnClickListener {
viewModel.doCount()
}
}
}
源码解析
核心类
- ViewModelProviders
- ViewModelProvider
- ViewModelStore
- FragmentActivity
一、ViewModel的创建
ViewModel的实例化很简单,代码如下
val viewModel = ViewModelProviders.of(activity).get(MyViewModel::class.java)
1) ViewModelProviders.of(activity, 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(activity.getViewModelStore(), factory);
}
参数也可以是Fragment,这里以Activity为例,先来看of方法的第二个参数 Factory
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
*
*
* @param modelClass a {@code Class} whose instance is requested
* @param The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
T create(@NonNull Class modelClass);
}
Factory 是 ViewModelProvider 的内部类,定义了一个 create 方法,作用就是创建一个 ViewModel 对象,这个很好理解。Factory 接口默认有两个实现类,分别是 NewInstanceFactory 和 AndroidViewModelFactory,其中 NewInstanceFactory 的实现就是通过反射调用构造器创建 ViewModel 对象,AndroidViewModelFactory 则是 NewInstanceFactory 的子类,在 NewInstanceFactory 的基础上多一个 Context 字段,以下是两个类的源码。
- NewInstanceFactory 源码
public static class NewInstanceFactory implements Factory {
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public T create(@NonNull Class 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);
}
}
}
- AndroidViewModelFactory 源码
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
/**
* Retrieve a singleton instance of AndroidViewModelFactory.
*
* @param application an application to pass in {@link AndroidViewModel}
* @return A valid {@link AndroidViewModelFactory}
*/
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
/**
* Creates a {@code AndroidViewModelFactory}
*
* @param application an application to pass in {@link AndroidViewModel}
*/
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
@NonNull
@Override
public T create(@NonNull Class 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);
}
}
我们继续回来看 ViewModelProviders.of(activity, 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(activity.getViewModelStore(), factory);
}
这里 factory 如果为 null,则会创建一个默认的 Factory 对象,类型就是上面讲到的 AndroidViewModelFactory。最后,of 方法返回了一个通过构造方法创建的 ViewModelProvider 对象,第一个参数是 activity.getViewModelStore(),这里的activity 是 FragmentActivity 类型
public class FragmentActivity extends ComponentActivity implements
ViewModelStoreOwner,
ActivityCompat.OnRequestPermissionsResultCallback,
ActivityCompat.RequestPermissionsRequestCodeValidator {
@NonNull
@Override
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;
}
}
在 FragmentActivity 的源码中可以看到,FragmentActivity 实现了
ViewModelStoreOwner 接口,接口中定义了 getViewModelStore() 方法,FragmentActivity 实现 getViewModelStore() 方法并返回了一个 ViewModelStore 对象。
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
在 ViewModelStore 中,定义了一个 HashMap 对象,存储的 key 是包含各 ViewModel 子类完整类名的字符串(在后面的源码中会看到),value 则是 ViewModel 对象。所以 ViewModelStore 其实就是一个容器,内部保存了所有的 ViewModel 实例。
public class ViewModelStore {
private final HashMap 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);
}
/**
* 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();
}
}
2) ViewModelProvider 类
ViewModelProviders.of() 方法最后创建并返回了一个 ViewModelProvider 对象,那我们就继续来看 ViewModelProvider 类,首先来看它的构造方法:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
在构造方法中保存了 ViewModelStore 和 Factory 对象,这两个参数在之前都已经介绍过了,ViewModelStore 用于保存 ViewModel 对象,Factory 用于创建 ViewModel 对象。
到这里,一个 ViewModelProvider 对象就创建好了,然后我们会调用 VideModelProvider 的 get 方法来获取 ViewModel 对象,看源码:
public class ViewModelProvider {
private static final String DEFAULT_KEY = "android.arch.lifecycle.ViewModelProvider.DefaultKey";
@NonNull
@MainThread
public T get(@NonNull Class modelClass) {
// canonicalName 是 modelClass 的完整类名(例如: com.evan.demo.MyViewModel)
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@NonNull
@MainThread
public T get(@NonNull String key, @NonNull Class 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 方法传入的参数应是 ViewModel 子类的 Class 对象,然后在 mViewModelStore 查看是否有缓存,如果有则直接返回,如果没有则通过 mFactory 创建一个并存入 mViewModelStore。
到这里为止,我们已经读完了获取 ViewModel 对象的源码。
小结:
- 通过 ViewModelProviders.of(activity, factory) 获取 ViewModelProvider 实例,ViewModelProvider 中包含一个 ViewModelStore 实例,用于保存已创建的 ViewModel 实例(ViewModelStore 通过 FragmentActivity 的 getViewModelStore() 方法获取),factory则是用于创建ViewModel实例
- 通过 ViewModelProvider.get(modelClass) 方法可以获取 ViewModel实例,实际就是从 ViewModelStore 容器中取出对应的 ViewModel实力缓存,如果没有则通过 factory 创建实例并存入 ViewModelStore
二、ViewModel 实现数据持久化原理
还记得么?之前有提到过,ViewModelStore 的实例是由 FragmentActivity 中的 getViewModelStore() 方法提供的,我们再来看源码:
public class FragmentActivity extends ComponentActivity implements
ViewModelStoreOwner,
ActivityCompat.OnRequestPermissionsResultCallback,
ActivityCompat.RequestPermissionsRequestCodeValidator {
@NonNull
@Override
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;
}
}
从代码中可以看到,FragmentActivity中有一个 mViewModelStore 字段,保存了 ViewModelStore 实例,我们可以在 FragmentActivity 的源码中对搜索 mViewModelStore,就可以找到几处对 mViewModelStore 的逻辑处理,这些代码的位置就是 ViewModel 能够做到状态保存的核心代码,我们一起来看一下。
/**
* Retain all appropriate fragment state. You can NOT
* override this yourself! Use {@link #onRetainCustomNonConfigurationInstance()}
* if you want to retain your own state.
*/
@Override
public final Object onRetainNonConfigurationInstance() {
// 开发者自定义的状态保存
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;
// 返回的 NonConfigurationInstances 对象不会被回收
return nci;
}
上面这段代码是最核心的部分,大家都知道 Activity 在横竖屏切换时会回调 onSaveInstanceState 方法用于保存数据,之后再通过 onCreate 或 onRestoreInstanceState 方法还原数据。但是,可能很多人不知道的是,在 Activity 中还有两个方法也同样可以用于保存Activity的状态,它们是 onRetainNonConfigurationInstance 和 getLastNonConfigurationInstance 方法。Activity 在重建时会回调 onRetainNonConfigurationInstance 方法,该方法会返回一个Object对象保存状态,而不再是Bundle类型了。在 Activity 恢复时可以调用 getLastNonConfigurationInstance() 方法获取最近一次 onRetainNonConfigurationInstance() 方法返回的 Object 对象用于恢复状态。
我们可以看到在源码中 onRetainNonConfigurationInstance 方法返回的是一个 NonConfigurationInstances 对象,NonConfigurationInstances 类很简单,只保存了三个对象,分别是custon(自定义状态)、viewModelStore、fragments(Activity中的所有Fragment的保存状态),既然 viewModelStore 跟着 NonConfigurationInstances 实例一起在 onRetainNonConfigurationInstance 方法中返回了,viewModelStore 自然也就被保留了下来,不会被在横竖屏切换时回收。
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
FragmentManagerNonConfig fragments;
}
那既然 viewModelStore 保存了下来,那肯定也会有恢复状态的逻辑,请看以下源码:
/**
* Perform initialization of all fragments.
*/
@SuppressWarnings("deprecation")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
mFragments.attachHost(null /*parent*/);
super.onCreate(savedInstanceState);
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
// ...
}
可以看到,在 FragmentActivity 的 onCreate 中调用 getLastNonConfigurationInstance 方法做了相应的处理
PS:细心的同学应该也注意到了 onRetainNonConfigurationInstance 方法源码中的注释,官方不允许开发者直接覆写onRetainNonConfigurationInstance()方法,应该该方法中已经了默认实现,其中就包括 ViewModelStore 实例的保存,如要开发者需要保存恢复其他数据,则应该覆写 onRetainCustomNonConfigurationInstance方法。
三、ViewModel 的资源回收
根据官方文档描述 ViewModel的生命周期范围涵盖了整个Activity或Fragment的生命周期,包括Activity重建触发的生命周期(比如屏幕旋转或配置更改等情况),当生命周期结束时,会回调onCleared方法,如下图所示:
FragmentActivity 在 onDestroy 方法中将 mViewModelStore 资源回收了
/**
* Destroy all fragments.
*/
@Override
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
mFragments.dispatchDestroy();
}
最后看 ViewModelStore 的源码,在 clear 方法中,调用了所有 ViewModel 实例的 onCleared() 方法
public class ViewModelStore {
private final HashMap 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);
}
/**
* 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();
}
}
至此,ViewModel 源码解析就结束了。
总结
ViewModel 的代码并不复杂,其核心代码其实就是通过 onRetainNonConfigurationInstance 方法进行了状态保存。