前言

ViewModel 是 Google 推荐使用的, 也是我们 App 中常用开发模式使用到的, 所以了解下它的原理还是非常有必要的。 我们不说它在架构的价值, 我们就分析它的源码和原理。

基本使用

1
2
3
4
5
6
7
8
9
10
class MainModel : ViewModel()

private val mModel by lazy {
ViewModelProvider(this, object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MainModel() as T
}

})[MainModel::class.java]
}

我们可以看到我们自己继承下 ViewModel 就定义好了自己对于界面的 ViewModel 层, 那么我们看看 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
public abstract class ViewModel {
// Can't use ConcurrentHashMap, because it can lose values on old apis (see b/37042460)
@Nullable
private final Map<String, Object> mBagOfTags = new HashMap<>();
private volatile boolean mCleared = false;

/**
* This method will be called when this ViewModel is no longer used and will be destroyed.
* <p>
* It is useful when ViewModel observes some data and you need to clear this subscription to
* prevent a leak of this ViewModel.
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}

@MainThread
final void clear() {
mCleared = true;
// Since clear() is final, this method is still called on mock objects
// and in those cases, mBagOfTags is null. It'll always be empty though
// because setTagIfAbsent and getTag are not final so we can skip
// clearing it
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
// see comment for the similar call in setTagIfAbsent
closeWithRuntimeException(value);
}
}
}
onCleared();
}

/**
* Sets a tag associated with this viewmodel and a key.
* If the given {@code newValue} is {@link Closeable},
* it will be closed once {@link #clear()}.
* <p>
* If a value was already set for the given key, this call does nothing and
* returns currently associated value, the given {@code newValue} would be ignored
* <p>
* If the ViewModel was already cleared then close() would be called on the returned object if
* it implements {@link Closeable}. The same object may receive multiple close calls, so method
* should be idempotent.
*/
@SuppressWarnings("unchecked")
<T> T setTagIfAbsent(String key, T newValue) {
T previous;
synchronized (mBagOfTags) {
previous = (T) mBagOfTags.get(key);
if (previous == null) {
mBagOfTags.put(key, newValue);
}
}
T result = previous == null ? newValue : previous;
if (mCleared) {
// It is possible that we'll call close() multiple times on the same object, but
// Closeable interface requires close method to be idempotent:
// "if the stream is already closed then invoking this method has no effect." (c)
closeWithRuntimeException(result);
}
return result;
}

/**
* Returns the tag associated with this viewmodel and the specified key.
*/
@SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
<T> T getTag(String key) {
if (mBagOfTags == null) {
return null;
}
synchronized (mBagOfTags) {
return (T) mBagOfTags.get(key);
}
}

private static void closeWithRuntimeException(Object obj) {
if (obj instanceof Closeable) {
try {
((Closeable) obj).close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

源码我一点都没有删除, 因为即清晰又简单, 代码里很少。 我们可以看到内部有一个 Map, 我们可以存储自己的一些数据, 然后有存, 肯定也有会取, 当然也会有清理。 不再更详细的分析了。
我们把注意力放在获取这个 ViewModel 上。 其步骤很简单, 就是构造一个 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
31
ViewModelProvider(this, object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MainModel() as T
}
})[MainModel::class.java]

public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
val canonicalName = modelClass.canonicalName
?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
return get("$DEFAULT_KEY:$canonicalName", modelClass)
}

public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
var viewModel = store[key]
if (modelClass.isInstance(viewModel)) {
(factory as? OnRequeryFactory)?.onRequery(viewModel)
return viewModel as T
} else {
@Suppress("ControlFlowWithEmptyBody")
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = if (factory is KeyedFactory) {
factory.create(key, modelClass)
} else {
factory.create(modelClass)
}
store.put(key, viewModel)
return viewModel
}

我们可以看出来好简单, get 的好简单, 如果没有传入 key, 就是用默认的, 从 store 里取而已, 没有就调用新建, 并设置进去。 不再过多详细的分析。
我们看下 store 是什么。

1
2
3
4
5
6
7
8
9
 public open class ViewModelProvider(
private val store: ViewModelStore,
private val factory: Factory
)

public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
owner.viewModelStore,
factory
)

我们可以看到, 是我们构造 ViewModelProvider 时传入进来的。 Factory 是我们如果构造 ViewModel 的工厂, 这个不再多说了。 我们看看我们传入的是什么。 穿的是 this, 是当前 activity, 接收的是所以这个 activity 肯定实现了 ViewModelStoreOwner, 然后调用了 owner.viewModelStore。 我们看看 Activity 中 getViewModelStore 的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}

mViewModelStore 是 Activity 的成员变量, 仅仅是实例化了一个 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
25
26
27
28
29
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);
}

Set<String> keys() {
return new HashSet<>(mMap.keySet());
}

/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}

你看看, 过于简单, 就不再分析了。 也是一个 map 去存储 ViewModel 而已。 我们看看什么时机清理的这些 ViewModel。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public ComponentActivity() {
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
}

可以看到在 Activity 初始化的时候监听了 Lifecycle 生命周期, 如果是 on_destroy 时清理的。
到这里应该整个 ViewModel 就分析完毕了。 很简单的设计。 但是还有从多个 ViewModel 是通过 key 存入到 Activity 里面的, 所以我们在一个 Activity 下的多个 Fragment 是可以通过 ViewModel 进行通信的, 因为如果 key 一样的话, 其 ViewModel 是一样的。