近期因为项目需要上Jetpack,所以研究了下Jetpack,这里是关于ViewModel源码解析,希望看完能帮助你提升对ViewModel的理解,但是以下内容不包含ViewModel+LiveData的使用。
依赖:
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
啰嗦一下,这个版本需要支持AndroidX哦(在gradle.properties中添加如下代码,将项目由原来的Android.supprt转移到Androidx上)
android.useAndroidX=true
android.enableJetifier=true
使用
使用方法很简单,三步走:
- 继承ViewModel,在自己的子类中写上管理UI相关的数据的逻辑,或者用来负责UI组件间的通信。
public class MyViewModel extends ViewModel {
// 自己的逻辑
}
- 在 Activity/Fragment中生成对应的 ViewModel
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
- 在你想要使用的地方调用对应的逻辑就ok。
源码解析
通过简单的继承和生成,他是如何与Activity/Fragment的生命周期挂上钩的呢?他是如何跨越Activity/Fragment的生命周期呢?ViewModel 的生命周期如何呢?源码见分晓。
下面先以Activity为例,注意 AppCompatActivity 是 FragmentActivity 的子类:
@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);
}
虽说只有几行,可是却很重要。checkApplication
一看返回值就是获取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;
}
很简单,直接获取Activity中存储的Application就可以了。但是如果你仔细看抛出的异常信息,就会奇怪,为什么获取不到 Application 就是还没执行 onCreate 呢?老道点的人都知道,Activity 的 Application 是在 ActivityThread 中的调用 Activity 的 attach 方法为 Activity.mAplication 赋值的,而调用时期是在 Activity的onCreate之前,所以抛出的异常会说,还没执行onCreate呢。
接着看源码,那这个 Factory 是什么呢?ViewModelProvider的内部接口:
/**
* Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
* 负责实例化 ViewModel
*/
public interface Factory {
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
如果使用过 ViewModel ,你就会知道,默认情况下的 Factory 是只能加载缺省构造器的 ViewModel,当我们要加载多种构造器的ViewModel就需要自己实现这个Fatory,并传进来。
那默认的 Factory怎么加载进来的呢?
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
机智的我一看方法名就知道是全局单例啦。我们来深入看一下这个在ViewModelProvider内部的静态内部类:
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance; // 全局单例
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication; // 存储 Application
public AndroidViewModelFactory(@NonNull Application application) { // public的构造器,可以new了
mApplication = application;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) { // 如果是 AndroidViewModel的子类
//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 {
@SuppressWarnings("ClassNewInstance")
@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);
}
}
}
很简单的一个 Factory,全局单例,直接使用类加载器进行加载。
书接上文,当获取到 AndroidViewModelFactory 的全局单例后,直接返回了刚new的ViewModelProvider
return new ViewModelProvider(activity.getViewModelStore(), factory);
这说明了一件事,每一个Activity/Fragment 都有一个 ViewModelProvider。这里传了两个参数,非常重要,一个是ViewModelStore,实质上就是一个存放 ViewModel 的 Hashmap,另一个就是 Factory,刚刚已经讲了。
当 new 这个 ViewModelProvider 的时候,构造器干嘛了?
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
就保存在了自己的局部变量中。
那有人就会问了 activity.getViewModelStore()
是干嘛的啊,怎么获取的啊?这就分析,因为他也与 ViewModel 的生命周期有关哦。
在看getViewModelStore()
的源码之前,我们先看下 ViewModelStore 是什么东西。
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);
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
大道至简,就是封装了put
、get
、clear
操作的 hashmap
。key:String(特定前缀+每个ViewModel的CanonicalName)。
接下来就是本篇的重头戏,如何 FragmentActivity#getViewModelStore 的:
private ViewModelStore mViewModelStore;
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) { // 判断是否执行了 onCreate()
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
作为 FragmentActivity 的 成员变量 mViewModelStore,全局就只有这里创建。那在第一次使用getViewModelStore()
的时候,都会为 null,那 getLastNonConfigurationInstance,是什么呢?就是能获取到非配置实例数据,并允许从前一个实例中提取到任何有用的实例数据。这里就存储了上一个Activity 的 ViewModelStore,那如果上一个实例并没有 ViewModelStore 那就直接new一个吧。
到这里,ViewModelProviders#of 就讲解完了,那接下来就是 ViewModelProvider.get(MyViewModel.class)
private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey";
@NonNull
@MainThread
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);
}
如果你对上文的 ViewModelStore#HashMap#key 不懂得话,这里就写得很清楚了。接着看:
@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;
}
去 ViewModelStore 中直接get,没有的话就使用Factory直接create,存储起来,并返回。
到这里我们调用的代码事情就干完了,那我们来看看系统为我们干嘛了。这里我们只需要追踪 ModelStore 就可以了,因为他才是存储的介质。
FragmentActivity#onCreate 默认帮我们存储了上一个实例的 ViewModelStore
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null && nc.viewModelStore != null && mViewModelStore == null) {
mViewModelStore = nc.viewModelStore;
}
FragmentActivity#onDestroy 帮我们清空(正常GC掉)
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
开始的时候提出的问题可以回答了,如何跨越Activity/Fragment的生命周期的呢?因为他会获取上一个实例的 ViewModelStore 数据。
一图解千惑,ViewModel的生命周期问题: