一、概述
ViewModel 作为 Android Jetpack 架构组件的核心,通过 生命周期感知 和 数据持久化 机制,解决了 Activity/Fragment 因配置变更(如屏幕旋转)或系统资源回收导致的数据丢失问题。其核心设计思想是将 数据逻辑 与 UI 控制器(Activity/Fragment)解耦,确保数据在组件重建时得以保留。以下是其实现原理的总结。
二、简单使用
ViewModel 的生命周期可以长于 Activity,很显然不能直接在 Activity 中直接新建一个实例。我们常用的方法:
ViewModelProvider(this)[MainViewModel::class.java]
三、 ViewModelProvider
kotlin的特性 []就相当于 get(),首先看到的是ViewModelProvider的实例化创建。
public actual open class ViewModelProvider private constructor(
private val impl: ViewModelProviderImpl,
){
public constructor(
owner: ViewModelStoreOwner,
) : this(
store = owner.viewModelStore,
factory = ViewModelProviders.getDefaultFactory(owner),
defaultCreationExtras = ViewModelProviders.getDefaultCreationExtras(owner)
)
public constructor(
store: ViewModelStore,
factory: Factory,
defaultCreationExtras: CreationExtras = CreationExtras.Empty,
) : this(ViewModelProviderImpl(store, factory, defaultCreationExtras))
}
viewModelStore,Factory,defaultCreationExtras三者会被包装成ViewModelProviderImpl。
关键的是viewModelStore,Factory两部分,我们逐一来看。
1.ViewModelStore
public open class ViewModelStore {
private val map = mutableMapOf<String, ViewModel>()
}
前面的构造函数中可见 this 也就是ViewModelStoreOwner,从而可以推测AppCompatActivity实现了ViewModelStoreOwner接口:
public class ComponentActivity implements ViewModelStoreOwner
public interface ViewModelStoreOwner {
public val viewModelStore: ViewModelStore
}
ViewModelStore就是个存储类,使用了个 HashMap来保存ViewModel。
ViewModelStoreOwner接口就只有一个叫viewModelStore变量。但是 kotlin 中public val代表只读变量,会自动包含 get 方法。
@NonNull
@Override
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.");
}
ensureViewModelStore();
return mViewModelStore;
}
这里注意mViewModelStore。其实ComponentActivity自带一个ViewModelStore类的变量。
public class ComponentActivity{
private ViewModelStore mViewModelStore;
}
ensureViewModelStore确保的是mViewModelStore不为空,这里就能看到ViewModelStore的实例化。
@SuppressWarnings("WeakerAccess") /* synthetic access */
void ensureViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
这里可以发现mViewModelStore可以从两个地方来,一个是 new出来的实例,另一个是从nc中获取。
这个问题先放着,下面再说。
2.Factory
默认的创建过程:
factory = ViewModelProviders.getDefaultFactory(owner)
internal fun getDefaultFactory(owner: ViewModelStoreOwner): ViewModelProvider.Factory =
if (owner is HasDefaultViewModelProviderFactory) {
owner.defaultViewModelProviderFactory
} else {
DefaultViewModelProviderFactory
}
也就是DefaultViewModelProviderFactory。
internal actual object DefaultViewModelProviderFactory : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: KClass<T>, extras: CreationExtras): T =
JvmViewModelProviders.createViewModel(modelClass.java)
}
总结一下:创建 ViewModelProvider 的过程中,它做了一下几件事情:
- 新建
DefaultViewModelProviderFactory里面包含create方法 。 - 传入了
ViewModelStore,是 activity 在ComponentActivity中的一个实现。
四、get()方法
public open operator fun <T : ViewModel> get(modelClass: Class<T>): T =
get(modelClass.kotlin)
@MainThread
public actual operator fun <T : ViewModel> get(modelClass: KClass<T>): T =
impl.getViewModel(modelClass)
最终是走到impl的getViewModel方法中,impl 是什么之前也放过,重新贴到下面:
public actual open class ViewModelProvider private constructor(
private val impl: ViewModelProviderImpl,
)
getViewModel的逻辑可以概括为:当前 store 中如果有这个所需 ViewModel的实例,就直接返回,如果没有就调用createViewModel并返回。
@Suppress("UNCHECKED_CAST")
internal fun <T : ViewModel> getViewModel(
modelClass: KClass<T>,
key: String = ViewModelProviders.getDefaultKey(modelClass),
): T {
val viewModel = store[key]
if (modelClass.isInstance(viewModel)) {
if (factory is ViewModelProvider.OnRequeryFactory) {
factory.onRequery(viewModel!!)
}
return viewModel as T
} else {
@Suppress("ControlFlowWithEmptyBody") if (viewModel != null) {
// TODO: log a warning.
}
}
val extras = MutableCreationExtras(extras)
extras[ViewModelProviders.ViewModelKey] = key
return createViewModel(factory, modelClass, extras).also { vm -> store.put(key, vm) }
}
这个就走到factory的create方法去了。
internal actual fun <VM : ViewModel> createViewModel(
factory: ViewModelProvider.Factory,
modelClass: KClass<VM>,
extras: CreationExtras
): VM {
return try {
factory.create(modelClass, extras)
} catch (e: AbstractMethodError) {
try {
factory.create(modelClass.java, extras)
} catch (e: AbstractMethodError) {
factory.create(modelClass.java)
}
}
}
回到之前的DefaultViewModelProviderFactory逻辑中。
最终走入反射的逻辑中,通过反射创建ViewModel实例。
internal object JvmViewModelProviders {
@Suppress("DocumentExceptions")
fun <T : ViewModel> createViewModel(modelClass: Class<T>): T =
try {
modelClass.getDeclaredConstructor().newInstance()
} catch (e: NoSuchMethodException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: InstantiationException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: IllegalAccessException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
}
}
五、配置更改
屏幕方向,多窗口模式,系统语言,字体缩放,暗黑模式,屏幕密度,键盘可用性都会导致Activity或Fragment的重建。
但ViewModel最大的特点之一就是实现了数据的稳定性,可以用来在屏幕旋转时保留数据。那这又是怎么实现的呢?
回到之前留下的问题:
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
返回什么取绝于mLastNonConfigurationInstances,接下来我们寻找一下这个变量。
public class Activity{
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
NonConfigurationInstances mLastNonConfigurationInstances;
}
当新 Activity 实例被创建时,系统会调用其 attach() 方法,并传递之前保存的 NonConfigurationInstances 对象:
// Activity.java
final void attach(
Context context,
ActivityThread aThread,
...,
NonConfigurationInstances lastNonConfigurationInstances, // 传递的数据
...
) {
mLastNonConfigurationInstances = lastNonConfigurationInstances; // 存储到成员变量
}
当 Activity 因配置变更被销毁时,系统会调用 onRetainNonConfigurationInstance() 方法。
ComponentActivity 重写了此方法,将 ViewModelStore 保存到 NonConfigurationInstances 对象中:
public final Object onRetainNonConfigurationInstance() {
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
这样也就实现了viewModelStore在配置更改时的保存效果。
六、生命周期
在ComponentActivity 中注册了一个回调getViewModelStore().clear(),发生在ON_DESTROY,并且是非isChangingConfigurations的时候ViewModelStore会被清空。
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
// Clear out the available context
mContextAwareHelper.clearAvailableContext();
// And clear the ViewModelStore
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
public fun clear() {
for (vm in map.values) {
vm.clear()
}
map.clear()
}