Jetpack源码解读(三)——ViewModel

390 阅读4分钟

一、概述

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))
	
}

viewModelStoreFactorydefaultCreationExtras三者会被包装成ViewModelProviderImpl

关键的是viewModelStoreFactory两部分,我们逐一来看。

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变量。但是 kotlinpublic 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 的过程中,它做了一下几件事情:

  1. 新建DefaultViewModelProviderFactory里面包含create方法 。
  2. 传入了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)

最终是走到implgetViewModel方法中,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) }
}

这个就走到factorycreate方法去了。

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)
	}
}

五、配置更改

屏幕方向,多窗口模式,系统语言,字体缩放,暗黑模式,屏幕密度,键盘可用性都会导致ActivityFragment的重建。

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()
}