简介
ViewModel类是被设计用来以可感知生命周期的方式存储和管理 UI 相关数据,ViewModel中数据会一直存活即使 activity configuration发生变化,比如横竖屏切换的时候。
数据持久化
我们知道在屏幕旋转的 时候 会经历 activity 的销毁与重新创建,这里就涉及到数据保存的问题,显然重新请求或加载数据是不友好的。在 ViewModel 出现之前我们可以用 activity 的onSaveInstanceState()机制保存和恢复数据,但缺点很明显,onSaveInstanceState只适合保存少量的可以被序列化、反序列化的数据,假如我们需要保存是一个比较大的 bitmap list ,这种机制明显不合适。
由于 ViewModel 的特殊设计,可以解决此痛点。
先来看下 ViewModel 生命周期图:
由图可知,ViewModel 生命周期是贯穿整个 activity 生命周期,包括 Activity 因旋转造成的重创建,直到 Activity 真正意义上销毁后才会结束。既然如此,用来存放数据再好不过了。
异步回调问题
通常我们 app 需要频繁异步请求数据,比如调接口请求服务器数据。当然这些请求的回调都是相当耗时的,之前我们在 Activity 或 fragment里接收这些回调。所以不得不考虑潜在的内存泄漏情况,比如 Activity 被销毁后接口请求才返回。处理这些问题,会给我们增添好多复杂的工作。
但现在我们利用 ViewModel 处理数据回调,可以完美的解决此痛点。
分担 UI controller负担
从最早的 MVC 到目前流行的 MVP、MVVM,目的无非是 明确职责,分离 UI controller 负担。
UI controller 比如 Activity 、Fragment 是设计用来渲染展示数据、响应用户行为、处理系统的某些交互。如果再要求他去负责加载网络或数据库数据,会让其显得臃肿和难以管理。所以为了简洁、清爽、丝滑,我们可以分离出数据操作的职责给 ViewModel。
Fragments 间共享数据
比如在一个 Activity 里有多个fragment,这fragment 之间需要做某些交互。我之前的做法是接口回调,需要统一在 Activity 里管理,并且不可避免的 fragment 之间还得互相持有对方的引用。仔细想想就知道这是很翔的一件事,耦合度高不说,还需要大量的容错判断(比如对方的 fragment 是否还活着)。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
33public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
仔细体会下这样的好处会发现:
1、Activity 不需要做任何事,甚至不知道这次交互,完美解耦。
2、Fragment 只需要 与ViewModel交互,不需要知道对方 Fragment 的状态甚至是否存在,更不需要持有其引用。所有当对方 Fragment 销毁时,不影响本身任何工作。
3、Fragment 生命周期互不影响,甚至 fragment 替换成其他的 也不影响这个系统的运作。
用法简介
ViewModel一般配合 LiveData 使用。
首先,获取 ViewModel 实例,通过提供的类ViewModelProviders:1
2
3
4
5MyViewModel model = ViewModelProviders.of(activity).get(MyViewModel.class);
或
MyViewModel model = ViewModelProviders.of(fragment).get(MyViewModel.class);
或带有 Factory 的
MyViewModel model = ViewModelProviders.of(activity,factory).get(MyViewModel.class);
VM 内部操作:1
2
3
4
5
6
7
8
9
10
11
12
13
14public 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.
}
}
然后,可在 activity 观察数据变化:1
2
3
4
5
6
7
8
9
10
11public 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
1 | public abstract class ViewModel { |
原来 ViewModel 是个抽象类,里面只有一个 onCleared() 方法。 onCleared() 会在 ViewModel 被销毁时回调,所以可以在 onCleared() 里面做一些释放资源、清理内存的操作。
另外,ViewModel 还有一个子类: AndroidViewModel 。AndroidViewModel 在 ViewModel 的基础上内部包含了 application 。
ViewModelProviders
我们就来抽丝剥茧了,先从 ViewModelProviders 入手。创建 ViewModel 时在 ViewModelProviders 中调用了 of 方法。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
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
public static ViewModelProvider of(@NonNull Fragment fragment) {
return of(fragment, null);
}
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
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);
}
of 方法可以分为两个入口,分别对应着 Fragment 和 Activity 。这也说明了 ViewModel 的作用域其实是分为两个维度的。但是这两个方法内部的代码很像,逻辑基本都是:
- 先去获取 application ;
- 创建 factory ;
- 创建 ViewModelProvider ,ViewModelProvider 顾名思义就是提供 ViewModel 的;
第一步就不用说了,直接进入第二步吧。Factory
Factory 是什么东东呢,说白了就是 ViewModel 的制造工厂。所有的 ViewModel 都是由 Factory 来创建出来的。1
2
3
4
5
6
7
8
9
10
11
12public 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
*/
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
Factory 是个接口,里面定义了 create 方法来创建 ViewModel 。来看看它的实现类 NewInstanceFactory 。
NewInstanceFactory
1 | /** |
其实没啥好说的,就是利用反射来创建实例了,是一个很简单的实现类。NewInstanceFactory 其实是创建普通 ViewModel 的工厂,而如果想创建 AndroidViewModel 的话,工厂就要选择 AndroidViewModelFactory 了。
AndroidViewModelFactory
1 | /** |
发现在 AndroidViewModelFactory 的 create 方法中,对创建 ViewModel 的方案做了兼容,所以 AndroidViewModelFactory 是同时适用于创建 ViewModel 和 AndroidViewModel 的。并且 AndroidViewModelFactory 是单例工厂,防止多次创建浪费内存。
额外补充一点,在 ViewModelProviders 中有一个内部类 DefaultFactory ,现在已经被打上废弃的标签了,可以猜出这个 DefaultFactory 应该是早期版本的默认工厂类,现在已经被 AndroidViewModelFactory 代替了。
ViewModelStores
到这里 Factory 就有了,那么就重点来看看 ViewModelStores.of(activity) 这段代码了。ViewModelStores 是根据作用域用来提供 ViewModelStore 的,而 ViewModelStore 的作用就是存储 ViewModel ,内部是利用 key/value 将 ViewModel 保存在 HashMap 中,方便读写,这里就不展示 ViewModelStore 的源码了,大家可以把 ViewModelStore 当作 HashMap 就行。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
34
35
36
37
38
39/**
* Factory methods for {@link ViewModelStore} class.
*/
"WeakerAccess") (
public class ViewModelStores {
private ViewModelStores() {
}
/**
* Returns the {@link ViewModelStore} of the given activity.
*
* @param activity an activity whose {@code ViewModelStore} is requested
* @return a {@code ViewModelStore}
*/
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
return holderFragmentFor(activity).getViewModelStore();
}
/**
* Returns the {@link ViewModelStore} of the given fragment.
*
* @param fragment a fragment whose {@code ViewModelStore} is requested
* @return a {@code ViewModelStore}
*/
public static ViewModelStore of(@NonNull Fragment fragment) {
if (fragment instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) fragment).getViewModelStore();
}
return holderFragmentFor(fragment).getViewModelStore();
}
}
根据 ViewModelProviders 的思路,ViewModelStores 也是分为了两个方法,对应着 Fragment 和 Activity 。
- 如果 Activity 和 Fragment 实现了 ViewModelStoreOwner 的接口,那么直接返回内部的 ViewModelStore 就行了
- 如果是之前老早版本的 Activity 或者 Fragment ,那么它们肯定是没有实现 ViewModelStoreOwner 接口的,那该怎么办呢?很简单,新创建一个 Fragment 来关联 ViewModelStoreOwner 就好了啊!
所以就有了 holderFragmentFor(activity) 和 holderFragmentFor(fragment) 这段了。HolderFragment
HolderFragment 实现了 ViewModelStoreOwner 接口,所以 HolderFragment 的作用就是代替了那些之前没有实现 ViewModelStoreOwner 接口的 Activity/Fragment 。这样,Activity/Fragment 也间接地拥有了 ViewModelStore 。
HolderFragment 的代码我们就只看 holderFragmentFor(activity) 这一段吧,holderFragmentFor(fragment) 也是类似的。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/**
* @hide
*/
(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
return sHolderFragmentManager.holderFragmentFor(activity);
}
static class HolderFragmentManager {
...
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 添加进 Activity 里面,这样 HolderFragment 就和 Activity 的生命周期关联在一起了。实际上获取的就是 HolderFragment 里面的 ViewModelStore 。每个 Activity 里面只有一个 HolderFragment 。
Fragment 也是同理,利用 getChildFragmentManager() 来往里添加 HolderFragment 。这里就不讲了,有兴趣的同学可以自己回去看看源码。
至此,用来创建 ViewModelProvider 的两个入参 ViewModelStore 和 Factory 都讲完了。
ViewModelProvider
创建出 ViewModelProvider 后,最后一步就是调用它的 get 方法返回 ViewModel 了。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
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;
}
get 方法很 easy ,就是利用 class 的 canonicalName 生成一个唯一的 key ,然后利用 key 去 mViewModelStore 中获取。如果有值就返回,否则就利用 factory 创建新的 ViewModel ,然后保存到 mViewModelStore 中并返回。
整个 ViewModel 的源码流程基本上就讲完了,其实并不复杂。回去多多体会,总能明白其中的奥秘。
Tip
ViewModel的onCleared什么时候回调
之前说过,ViewModel 是保存在 ViewModelStore 里面的,所以 ViewModel 的销毁一定是在 ViewModelStore 里面操作的。
ViewModelStore
1 | /** |
可以看到 ViewModelStore 的 clear() 方法内部调用 ViewModel 的 onCleared() 方法。那么哪里调用了 ViewModelStore 的 clear() 方法呢?
Fragment
1 | /** |
可以从代码上看到,Fragment 的销毁操作调用是在 onDestroy() 中。
另外,如果状态保存标记值 mStateSaved 为 true 的情况下,是不会去清除 ViewModel 的,这也是为什么上面中讲的配置改变的情况下,数据得以保持住的原因。
FragmentActivity
1 | /** |
同理, Activity 的销毁操作也是在 onDestroy() 完成的。