本文是深刻理解「Android Architecture Components」系列文章第四篇 源码基于 AAC 1.1.1 版本html
【AAC 系列一】Android 应用架构新时代来临!java
【AAC 系列二】深刻理解架构组件的基石:Lifecycleandroid
这个连接不要点,有毒,千万不要github
在上一篇 LiveData
原理分析一文中,咱们提到了 ViewModel
,它跟 LiveData 配合可以把价值发挥到最大。缓存
这一篇,咱们就来深刻浅出一下 ViewModel ,来说讲 ViewModel 的使用方式、生命周期、以及它的实现原理。架构
在深刻讲解 ViewModel 以前,先来简单了解下 ViewModel:app
The
ViewModel
class is designed to store and manage UI-related data in a lifecycle conscious way. TheViewModel
class allows data to survive configuration changes such as screen rotations.async
ViewModel 被设计来管理跟 UI 相关的数据, 而且可以感知生命周期;另外 ViewModel 可以在配置改变
的状况下让数据得以保留。ViewModel 重在以感知生命周期的方式
管理界面相关的数据。ide
咱们知道相似旋转屏幕等配置项改变会致使咱们的 Activity 被销毁并重建,此时 Activity 持有的数据就会跟随着丢失,而ViewModel 则并不会被销毁
,从而可以帮助咱们在这个过程当中保存数据,而不是在 Activity 重建后从新去获取。而且 ViewModel 可以让咱们没必要去担忧潜在的内存泄露问题
,同时 ViewModel 相比于用onSaveInstanceState()
方法更有优点,好比存储相对大的数据,而且不须要序列化以及反序列化。
总之 ViewModel,优势多多,接下去咱们介绍下 ViewModel 的基本使用。
ViewModel 的使用也很是简单,Android 提供了一个 ViewModel 类让咱们去继承,而且提供了 ViewModelProviders
来帮助咱们实例化 ViewModel。
搬运一个官网例子以下:
a):先添加一下依赖:
def lifecycle_version = "1.1.1"
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
复制代码
b):自定义一个MyViewModel
继承自ViewModel
,而且包含了一个 LiveData
:
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<User>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
复制代码
c):在 Activity 中借助 ViewModelProviders
得到 ViewModel 的实例,并借助 LiveData 订阅 users 的变化通知:
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
}
}
复制代码
就这样简单的步骤,咱们就使用上了 ViewModel,即使 MyActivity 从新建立,MyActivity 拿到的 MyViewModel 都会是一个实例。
那么问题来了, ViewModel 的生命周期究竟是怎么样的呢?
它背后蕴藏什么原理呢?我们接下来看看。
咱们在前面提到过,ViewModel 并不会由于 Activity 的配置改变销毁而一块儿销毁,那么 ViewModel 的生命周期究竟是怎么样的呢?
看一张官网给的图:
能够看到 ViewModel 在 Activity 的重建时依然存活。
Why
?
回顾下咱们以前使用 ViewModel 的代码:
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
复制代码
这里先从 ViewModelProviders.of
方法入手看看:
//ViewModelProviders
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
//传入了 null
return of(activity, null);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
//检查合法性
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
//走到这里,返回了 AndroidViewModelFactory 的单例
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
复制代码
ViewModelProviders.of()
方法帮咱们在默认状况下构建了一个 AndroidViewModelFactory
工厂类,来帮助建立 ViewModel,而且返回了一个在当前 Activity 生命周期内的 ViewModelProvider
。
看一下 AndroidViewModelFactory
:
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;
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 (NoSuchMethodException e) {
//...
}
}
return super.create(modelClass);
}
}
复制代码
AndroidViewModelFactory 其实就是一个经过反射
方法来构建 ViewModel 的工厂类,且是个单例。
看下来发现咱们 ViewModel 的 class 是传给了 ViewModelProvider.get()
方法。
来看看 get 方法:
//ViewModelProvider
@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");
}
//每一个类都有一个惟一的 key
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//先从 store 中获取
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;
}
复制代码
解释下代码,发现它为每一个 ViewModel 的 class 都构建了一个惟一的 key
, 并经过这个 key 尝试去一个叫作 ViewModelStore
的类获取 ViewModel 的实例,若是为null ,那么会经过 Factory 去建立,并把新的实例存入到 ViewModelStore。
主要的流程彷佛就跟咱们平时作缓存同样,好像没什么特别的东西,没有看到一丝跟生命周期相关的处理的代码,这是怎么回事?
再仔细思考一下,get 方法会优先从这个 ViewModelStore 中去拿,那么理论上只要保证 ViewModelStore 这个类在配置变化的过程当中没有被销毁,那么就能够保证咱们建立的 ViewModel 不会被销毁,咱们确定漏掉了关于 ViewModelStore 的重要东西。
回过去再仔细看一下,ViewModelProvider 的构建好像并不简单:
new ViewModelProvider(ViewModelStores.of(fragment), factory);
复制代码
这里传入了一个 经过 ViewModelStores 类建立的 ViewModelStore,而且传入了 fragment,必定有蹊跷。
//ViewModelStores
public static ViewModelStore of(@NonNull Fragment fragment) {
if (fragment instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) fragment).getViewModelStore();
}
return holderFragmentFor(fragment).getViewModelStore();
}
复制代码
这里又多出来了几个类 ViewModelStoreOwner、HolderFragment ,让咱们来追查一下。
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
复制代码
ViewModelStoreOwner 跟 LifecycleOwner 相似,只是个接口定义,重点来看看holderFragmentFor(fragment)
方法返回的 HolderFragment
。
//HolderFragment
public static HolderFragment holderFragmentFor(Fragment fragment) {
return sHolderFragmentManager.holderFragmentFor(fragment);
}
复制代码
方法又走到了 HolderFragmentManager 类,怎么又多了个 HolderFragmentManager ,神烦啊。
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;
}
}
复制代码
从源码中咱们能够获知 HolderFragmentManager
主要作这几件事:
在咱们想要获取 ViewModel 实例的时候,会先构建一个 HolderFragment
,将它添加咱们的宿主(Activity/Fragment)中
,并将它缓存起来;
同时经过注册回调来监听宿主的生命周期,Activity 对应 ActivityLifecycleCallbacks
,Fragment 对应 FragmentLifecycleCallbacks
,在宿主销毁的时候清理缓存;
类如其名,HolderFragmentManager
负责管理 HolderFragment,看到它注入了 HolderFragment,接下去看看 HolderFragment。
HolderFragment
源码精简以下:
public class HolderFragment extends Fragment implements ViewModelStoreOwner {
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 onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
}
复制代码
HolderFragment 内部持有了一个ViewModelStore
,而且实现了咱们以前提到的 ViewModelStoreOwner
接口,而且最为主要的是这段代码:
public HolderFragment() {
setRetainInstance(true);
}
复制代码
Fragment.setRetainInstance(true)
方法能够实现的效果为,在 Activity 配置改变后依然保存。
到这里 ViewModel 实现的原理就清晰了:经过注入一个retainInstance 为 true 的 HolderFragment ,利用 Fragment 的特性来保证在 Activity 配置改变后依然可以存活一下,而且保证了 HolderFragment 内部的 ViewModelStore 的存活,最终保证了 ViewModelStore 内部储存的 ViewModel 缓存存活,从而实现了 ViewModel 的生命周期这个特色功能。(又是 Fragment!)
ViewModel 重点类类图:
ViewModel原理实现序列图:
重点类讲解:
ViewModel
,抽象类,用来负责准备和管理 Activity/Fragment 的数据,而且还能处理 Activity/Fragment 跟外界的通讯,一般还存放业务逻辑,相似 Presenter;ViewModel 一般会暴露 LiveData 给 Activity/Fragment;而且 Activity 配置改变并不会致使 ViewModel 回收;AndroidViewModel
,一个会持有 Application
的 ViewModel;ViewModelStore
,负责存储 ViewModel 的类,而且还负责在 ViewModel 被清除以前通知它,也即调用 ViewModel.onCleared()
;ViewModelStoreOwner
, 是抽象 “ViewModelStore 的拥有者” 的接口定义,相似 LifecycleOwner 的角色,实现了它的有 HolderFragment、FragmentActivity;HolderFragment
,一个 retainInstance
属性为true
并实现了 ViewModelStoreOwner
的 Fragment,用来保存 ViewModelStore
,并保证它在配置修改时不被销毁;HolderFragmentManager
,负责建立、注入、缓存等管理 HolderFragment 的工做;ViewModel 原理总结:
经过注入一个 retainInstance
为true
的 HolderFragment ,利用 Fragment 的特性来保证在 Activity 配置改变后依然可以存活下来,而且保证了 HolderFragment 内部的 ViewModelStore 的存活,最终保证了 ViewModelStore 内部储存的 ViewModel 缓存存活,从而实现 ViewModel 在 Activity 配置改变的状况下不销毁的功能。
ViewModel 的使用注意事项:
不要持有 Activity
:ViewModel 不会由于 Activity 配置改变而被销毁,因此绝对不要持有那些跟 Activity 相关的类,好比Activity 里的某个 View,让 ViewModel 持有 Activity 会致使内存泄露,还要注意的是连 Lifecycle 也不行;不能访问 UI
:ViewModel 应该只负责管理数据,不能去访问 UI,更不能持有它;ViewModel 利用 Fragment 的特性,提供给咱们一个方式在特定的生命周期内去管理跟 UI 相关的数据;可以帮助咱们把数据管理的逻辑从 Activity/Fragment 中剥离开。
实际上 ViewModel 不只能够管理数据,并且还能够存放业务逻辑处理的代码,另外还可以方便 Activity 中的不一样Fragment 之间互相通讯,这个解决了以往咱们 Fragment 之间通讯的一个大问题。
深刻了解完 Lifecycle、LiveData、ViewModel 以后,能够发现它们确实很是强大,可以切实的帮助咱们解决实际开发过程当中遇到的问题。
强烈推荐你们赶忙上车体验,再晚就买不到票了啊。