ViewModel简单解析

ViewModel简单解析

为什么使用ViewModel

进行Android开发一段时间后,大多数人会发现Activity承载着繁多的任务,数据储存,ui控制都是它。其中数据存储是重中之重,当屏幕发生旋转,Activity改变大小时,在不做任何配置下,Activity会进行recreate,之前的数据没了。当然,自己可以手动进行在onConfigurationChanged对数据进行处理,除此之外,数据根据Activity/Fragment的生命周期处理也是繁琐的地方,出现的MVPMVVM都是对此进行的解决方案,而我认为,MVP使用过于繁琐让人捉摸不透,那么MVVM才是当前解决数据分离的最佳方案。

前置知识

LifeCycle LiveData Kotlin Kotlin-Coroutines

使用

1
2
3
viewModel = ViewModelProviders.of(this).get(MainPageViewModel::class.java)
viewModel = ViewModelProviders.of(this, factory).get(MainPageViewModel::class.java) // 还可通过factory定制ViewModel

初探ViewModel

可以看到ViewModel只是一个抽象类,我们平时使用都不会是直接new出来的,都是通过ViewModelProviders构造出的一个ViewModel实例。类里没多少东西,mBagOfTags对象和clear()方法比较令人在意,明显是当ViewModel要被清除的时候所做的工作,我们下面直接看一开始调用的ViewModelProviders

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
public abstract class ViewModel {
@Nullable
private final Map<String, Object> mBagOfTags = new HashMap<>();
private volatile boolean mCleared = false;
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
@MainThread
final void clear() {
mCleared = true;
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
closeWithRuntimeException(value);
}
}
}
onCleared();
}
@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) {
closeWithRuntimeException(result);
}
return result;
}
@SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
<T> T getTag(String key) {
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);
}
}
}
}

看到of()方法就是我们调用链的第一条,其中ViewModelProviders把真正的Provider委托给了ViewModelProvider(注意没有s),进阶一点的ViewModel用法是可以自己定义factory去生成我们的ViewModel实例的,明显的是如果我们不传自己的factory也需要一个默认的AndroidViewModelFactory去帮助我们构建。

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
public class ViewModelProviders {
...
@NonNull
@MainThread
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(fragment.getViewModelStore(), 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);
}
}

AndroidViewModelFactoryViewModelProvider的内部类,create()方法通过传进来的ModelClass反射构建我们需要的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
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
...
@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) {
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);
}
}

ViewModelProvider中,我们看到了熟悉的get()方法,这里利用拿到的ViewModelStore转成我们实际调用到的ViewModel实例,而且会确保这个ViewModel实例是一个单例,那么ViewModelStore从何而来呢?正是上面ViewModelProvidersof()方法中Fragment/FragmentActivity中储存的ViewModelStore,这个ViewModelStore实际上是一个HashMap。这不是又把ViewModel放在Activity/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
public class ViewModelProvider {
...
@SuppressWarnings("unchecked")
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
}

可以看到在onCreate()方法里会获取上一次的NonConfigurationInstances,当然前提是要有,onRetainNonConfigurationInstance储存了当前保存的ViewModel。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FragmentActivity
protected void onCreate(@Nullable Bundle savedInstanceState) {
.....
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
.....
}
public final Object onRetainNonConfigurationInstance() {
.......
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = mViewModelStore;
nci.fragments = fragments;
return nci;
}

诶?这不是跟网文说的用Fragment储存状态不一样吗?因为这是AndroidX转换后的实现,传说中的Fragment大法是在AndroidX转换前中实现的。

其中of()方法的返回值变成了在holderfragment持有的viewModelStore,由于实现都使用的是这个holderfragment,这里也就不需要ViewModelProvider来区分Activity与Fragment,去获取他们自己持有的ViewModelStore

1
2
3
4
5
6
7
8
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
// 这里变成了holderfragment
return holderFragmentFor(activity).getViewModelStore();
}

之所以会用到Fragment大法,是因为在Fragment中有一个setRetainInstance()的方法,在Activity重新创建的时候,可以跳过onDestroy()的生命周期,直接Detach掉(注意此时Fragment并未销毁),重新创建Activity的时候还可以重新Attach(注释及源码均来自SDK29),很好的利用了Fragment的这一特性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Control whether a fragment instance is retained across Activity
* re-creation (such as from a configuration change). This can only
* be used with fragments not in the back stack. If set, the fragment
* lifecycle will be slightly different when an activity is recreated:
* <ul> * <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
* will be, because the fragment is being detached from its current activity).
* <li> {@link #onCreate(Bundle)} will not be called since the fragment
* is not being re-created.
* <li> {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} <b>will</b>
* still be called.
* </ul>
*/
public void setRetainInstance(boolean retain) { mRetainInstance = retain; }

When ViewModel onClear

还记的开头提到的mBagOfTags对象和clear()方法吗?我们知晓了ViewModel是如何实例化并使用,那么反过来也需要知道ViewModel是如何清除的,我们从ViewModel-ktx的包可以知道,对于清除ViewModel任务,使用了kotlin的扩展,将所有将要被取消的任务丢进协程里进行取消,并且是在主线程完成的。

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
private const val JOB_KEY = "androidx.lifecycle.ViewModelCoroutineScope.JOB_KEY"
/**
* [CoroutineScope] tied to this [ViewModel].
* This scope will be canceled when ViewModel will be cleared, i.e [ViewModel.onCleared] is called
*
* This scope is bound to [Dispatchers.Main]
*/
val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main))
}
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}

延迟委托

在ktx包另一个名为ViewModelProvider.kt的文件中,我们实际上使用的get()方法是具有延迟的委托属性,这里如果没有创建对应的ViewModel,这里会传入类型为FactoryfactoryProducer对象进行创建,如果存在,这里还会检查一下缓存,真没有的话还会用类型为ViewModelStorestoreProducer的对象找是否有存储这个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
/**
* Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
* an activity), associated with this `ViewModelProvider`.
*
* @see ViewModelProvider.get(Class)
*/
@MainThread
inline fun <reified VM : ViewModel> ViewModelProvider.get() = get(VM::class.java)
/**
* An implementation of [Lazy] used by [androidx.fragment.app.Fragment.viewModels] and
* [androidx.activity.ComponentActivity.viewmodels].
*
* [storeProducer] is a lambda that will be called during initialization, [VM] will be created
* in the scope of returned [ViewModelStore].
*
* [factoryProducer] is a lambda that will be called during initialization,
* returned [ViewModelProvider.Factory] will be used for creation of [VM]
*/
class ViewModelLazy<VM : ViewModel> (
private val viewModelClass: KClass<VM>,
private val storeProducer: () -> ViewModelStore,
private val factoryProducer: () -> ViewModelProvider.Factory
) : Lazy<VM> {
private var cached: VM? = null
override val value: VM
get() {
val viewModel = cached
return if (viewModel == null) {
val factory = factoryProducer()
val store = storeProducer()
ViewModelProvider(store, factory).get(viewModelClass.java).also {
cached = it
}
} else {
viewModel
}
}
override fun isInitialized() = cached != null
}
分享到: