1. 概述
在 Android 开发中,ViewModel 的创建通常通过 ViewModelProvider 来完成。下面的示例代码展示了如何创建一个 ViewModel 实例:
示例代码:
val viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
可以看到,ViewModel 的创建过程是由 ViewModelProvider 完成的。
2. ViewModelProvider
ViewModelProvider 提供了一个主构造函数和两个次构造函数,如下:
主构造函数
public open class ViewModelProvider @JvmOverloads constructor(
private val store: ViewModelStore,
private val factory: Factory,
private val defaultCreationExtras: CreationExtras = CreationExtras.Empty,
)
在创建 ViewModelProvider 对象时,需要传入三个参数:ViewModelStore、ViewModelProvider.Factory 和 CreationExtras。
次构造函数
public constructor(owner: ViewModelStoreOwner) : this(
owner.viewModelStore,
defaultFactory(owner),
defaultCreationExtras(owner)
)
public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
owner.viewModelStore,
factory,
defaultCreationExtras(owner)
)
通常我们使用次构造函数来创建 ViewModelProvider 对象,只需传入 ViewModelStoreOwner 参数。在次构造函数中,通过 owner: ViewModelStoreOwner 获取默认的 ViewModelStore、Factory 和 CreationExtras,然后调用主构造函数创建 ViewModelProvider 对象。
ViewModelProvider#get 方法
ViewModelProvider 提供了 get 方法用于获取 ViewModel 实例,如下:
@MainThread
public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
val canonicalName = modelClass.canonicalName
?: throw IllegalArgumentException("本地类和匿名类不能是 ViewModel")
return get("$DEFAULT_KEY:$canonicalName", modelClass)
}
@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
// 从 ViewModelStore 中获取与 key 对应的 ViewModel 实例
val viewModel = store[key]
// 如果获取的实例与 modelClass 类型匹配,则返回该实例
if (modelClass.isInstance(viewModel)) {
(factory as? OnRequeryFactory)?.onRequery(viewModel)
return viewModel as T
} else {
@Suppress("ControlFlowWithEmptyBody")
if (viewModel != null) {
// TODO: 记录警告日志
}
}
// 创建额外参数用于 ViewModel 的创建
val extras = MutableCreationExtras(defaultCreationExtras)
extras[VIEW_MODEL_KEY] = key
// 使用 factory 创建 ViewModel 实例,并处理可能的反序列化问题
return try {
factory.create(modelClass, extras)
} catch (e: AbstractMethodError) {
factory.create(modelClass)
}.also {
// 将新创建的 ViewModel 实例放入存储中
store.put(key, it)
}
}
在 get 方法中:
- 尝试从 ViewModelStore 中获取与 key 对应的 ViewModel 实例。
- 如果获取的 ViewModel 实例与 modelClass 类型匹配,则返回该实例。
- 如果获取的 ViewModel 实例与 modelClass 类型不匹配,则使用 ViewModelProvider.Factory 创建一个新的 ViewModel 实例。
到这里,我们大概可以理解 ViewModelProvider 主构造函数中三个参数的作用:
- ViewModelStore:用于存储和管理 ViewModel 实例。
- ViewModelProvider.Factory:用于创建 ViewModel 实例的工厂类。
- CreationExtras:用于在创建 ViewModel 时传递额外参数。
接下来我们将深入分析 ViewModelStore、ViewModelProvider.Factory 和 CreationExtras 的创建过程及其内部实现。
3. ViewModelStore 创建过程及介绍
创建过程
在 ViewModelProvider 的次构造函数中,ViewModelStore 是通过 ViewModelStoreOwner 接口的 getViewModelStore 方法获取的。如下:
public constructor(
owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
ComponentActivity 实现了 ViewModelStoreOwner 接口,如下:
public class ComponentActivity implements ViewModelStoreOwner {
private ViewModelStore mViewModelStore;
@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;
}
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();
}
}
}
/**
* 当 Activity 即将因为配置更改(如屏幕旋转、语言改变等)而被销毁时,
* 系统会调用 onRetainNonConfigurationInstance。
* 该方法允许 Activity 保存一些状态,以便在新创建的 Activity 实例中恢复这些状态。
*/
@Override
@Nullable
@SuppressWarnings("deprecation")
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
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;
}
}
可以发现,Activity 中创建 ViewModelStore 的过程如下:
- 从
NonConfigurationInstances中取出viewModelStore。 - 如果取出的
viewModelStore为空,则创建ViewModelStore。 - 当 Activity 即将因为配置更改(如屏幕旋转、语言改变等)而被销毁时,系统会调用
onRetainNonConfigurationInstance。 - 在
onRetainNonConfigurationInstance中,将viewModelStore保存到NonConfigurationInstances中。
ViewModelStore 介绍
ViewModelStore 类用于存储和管理 ViewModel 实例:
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());
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
关键点总结:
ViewModelStore通过HashMap存储 ViewModel 实例。put方法用于存储 ViewModel,如果同一个 key 已经存在旧的 ViewModel 实例,会调用旧实例的onCleared方法。get方法用于获取指定 key 的 ViewModel 实例。clear方法清除所有存储的 ViewModel 实例,并调用它们的onCleared方法。
4. ViewModelProvider.Factory
创建过程
在 ViewModelProvider 的次构造函数中,调用了 defaultFactory 方法获取默认的 Factory 实例。defaultFactory 方法位于 ViewModelProvider 的内部类 AndroidViewModelFactory 中,如下:
internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
if (owner is HasDefaultViewModelProviderFactory)
owner.defaultViewModelProviderFactory
else instance
在 defaultFactory 方法中,首先判断 owner 是否实现了 HasDefaultViewModelProviderFactory 接口。如果实现了,则直接返回 owner 的 defaultViewModelProviderFactory 属性。否则,返回一个默认的 Factory 实例。
当在 Activity 中创建 ViewModel 时,Activity 实现了 HasDefaultViewModelProviderFactory 接口,因此会返回 Activity 的 defaultViewModelProviderFactory 属性,如下:
public interface HasDefaultViewModelProviderFactory {
@NonNull
ViewModelProvider.Factory getDefaultViewModelProviderFactory();
@NonNull
default CreationExtras getDefaultViewModelCreationExtras() {
return CreationExtras.Empty.INSTANCE;
}
}
public class ComponentActivity implements HasDefaultViewModelProviderFactory {
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null
);
}
return mDefaultFactory;
}
}
在上面分析过,ViewModelProvider 的 get 方法中,通过 factory 的 create 方法创建 ViewModel 实例。那么如果是在 Activity 中创建 ViewModel,就会调用 SavedStateViewModelFactory 的 create 方法,如下:
class SavedStateViewModelFactory : ViewModelProvider.OnRequeryFactory, ViewModelProvider.Factory {
private var application: Application? = null
private val factory: ViewModelProvider.Factory
constructor(application: Application?, owner: SavedStateRegistryOwner, defaultArgs: Bundle?) {
this.application = application
factory = if (application != null) getInstance(application)
else ViewModelProvider.AndroidViewModelFactory()
}
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
val key = extras[ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY]
?: throw IllegalStateException(
"VIEW_MODEL_KEY must always be provided by ViewModelProvider"
)
return if (extras[SAVED_STATE_REGISTRY_OWNER_KEY] != null &&
extras[VIEW_MODEL_STORE_OWNER_KEY] != null) {
val application = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]
// 是否是AndroidViewModel
val isAndroidViewModel = AndroidViewModel::class.java.isAssignableFrom(modelClass)
val constructor: Constructor<T>? = if (isAndroidViewModel && application != null) {
// 用于确定ViewModel构造函数中是否有SavedStateHandle
findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE)
} else {
// 用于确定ViewModel构造函数中是否有SavedStateHandle
findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE)
}
// 如果ViewModel没有使用SavedStateHandle
if (constructor == null) {
// 使用AndroidViewModelFactory创建ViewModel
return factory.create(modelClass, extras)
}
if (isAndroidViewModel && application != null) {
newInstance(modelClass, constructor, application, extras.createSavedStateHandle())
} else {
newInstance(modelClass, constructor, extras.createSavedStateHandle())
}
} else {
throw IllegalStateException(
"SAVED_STATE_REGISTRY_OWNER_KEY and VIEW_MODEL_STORE_OWNER_KEY must be provided in the creation extras to successfully create a ViewModel."
)
}
}
}
在 create 方法中,首先判断 ViewModel 是否使用了 SavedStateHandle。如果使用了,则通过 SavedStateViewModelFactory 的 newInstance 方法创建 ViewModel 实例;否则,使用 AndroidViewModelFactory 创建 ViewModel 实例。
SavedStateViewModelFactory 的 newInstance 方法如下:
internal fun <T : ViewModel?> newInstance(
modelClass: Class<T>,
constructor: Constructor<T>,
vararg params: Any
): T {
return try {
constructor.newInstance(*params)
} catch (e: IllegalAccessException) {
// 处理异常
}
}
AndroidViewModelFactory 的 create 方法如下:
@Suppress("DocumentExceptions")
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
return if (application != null) {
modelClass.getConstructor(Application::class.java).newInstance(app)
} else {
val application = extras[APPLICATION_KEY]
if (application != null) {
modelClass.getConstructor(Application::class.java).newInstance(application)
} else {
modelClass.newInstance()
}
}
}
关键点总结:
- 在
defaultFactory方法中,判断owner是否实现了HasDefaultViewModelProviderFactory接口,决定返回的 Factory 实例。 - 如果是在 Activity 中创建 ViewModel,通常会调用
SavedStateViewModelFactory的create方法创建 ViewModel 实例。 SavedStateViewModelFactory的create方法会根据 ViewModel 是否需要SavedStateHandle来决定使用哪种方式创建实例。- 如果不需要
SavedStateHandle,则使用AndroidViewModelFactory创建 ViewModel 实例。 - 最终都会通过反射创建ViewModel实例。
5. CreationExtras 创建过程及介绍
创建过程
CreationExtras 是在创建 ViewModel 时传递额外参数的一个对象。在 ViewModelProvider 的主构造函数中,默认传入的是 CreationExtras.Empty。当使用次构造函数时,会通过 defaultCreationExtras 方法生成相应的 CreationExtras 实例。
在 ViewModelProvider 类中,defaultCreationExtras 方法的定义如下:
internal fun defaultCreationExtras(owner: ViewModelStoreOwner): CreationExtras {
if (owner is HasDefaultViewModelProviderFactory) {
return owner.defaultViewModelCreationExtras
}
return CreationExtras.Empty
}
public class ComponentActivity implements HasDefaultViewModelProviderFactory {
public CreationExtras getDefaultViewModelCreationExtras() {
MutableCreationExtras extras = new MutableCreationExtras();
if (getApplication() != null) {
extras.set(ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY, getApplication());
}
extras.set(SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY, this);
extras.set(SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY, this);
if (getIntent() != null && getIntent().getExtras() != null) {
extras.set(SavedStateHandleSupport.DEFAULT_ARGS_KEY, getIntent().getExtras());
}
return extras;
}
}
CreationExtras的创建过程和ViewModelProvider.Factory的创建过程类似。
CreationExtras 介绍
CreationExtras 是一个接口,它用于在创建 ViewModel 时提供额外的参数。以下是 CreationExtras 的定义:
public interface CreationExtras {
CreationExtras Empty = new CreationExtras() {
// Empty implementation
};
// 可以添加需要的键值对等方法
}
自定义 CreationExtras
当需要传递额外参数时,可以自定义实现 CreationExtras 接口。以下是一个自定义实现的示例:
public class MyCreationExtras implements CreationExtras {
private final Map<String, Object> extras = new HashMap<>();
public void put(String key, Object value) {
extras.put(key, value);
}
public Object get(String key) {
return extras.get(key);
}
}
在创建 ViewModel 时,可以将 MyCreationExtras 传递给 ViewModelProvider。
使用示例
以下是如何使用自定义 CreationExtras 的示例:
MyCreationExtras extras = new MyCreationExtras();
extras.put("my_key", "my_value");
ViewModelProvider.Factory factory = new ViewModelProvider.NewInstanceFactory();
ViewModelProvider provider = new ViewModelProvider(this, factory, extras);
MyViewModel viewModel = provider.get(MyViewModel.class);
在这个示例中,我们创建了一个 MyCreationExtras 实例并添加了一些参数,然后将其传递给 ViewModelProvider,最终创建了一个 MyViewModel 实例。
总结
本文深入分析了 ViewModel 在 Android 中的创建过程,详细介绍了 ViewModelProvider、ViewModelStore、ViewModelProvider.Factory 以及 CreationExtras 的实现和作用。通过对示例代码的剖析,我们可以清晰地了解以下内容:
- ViewModelProvider:通过主构造函数和次构造函数创建 ViewModelProvider 对象,传递必要的参数(ViewModelStore、ViewModelProvider.Factory 和 CreationExtras),实现了对 ViewModel 实例的管理和提供。
- ViewModelStore:用于存储和管理 ViewModel 实例,确保在配置更改时(如屏幕旋转)能正确地保留和恢复 ViewModel。
- ViewModelProvider.Factory:负责创建 ViewModel 实例,支持带有 SavedStateHandle 的 ViewModel,确保在特定场景下(如需要保存状态)能够正确创建 ViewModel 实例。
- CreationExtras:用于在创建 ViewModel 时传递额外的参数,提供灵活的扩展能力,满足不同场景下的需求。
通过本文的分析,读者可以更深入地理解 ViewModel 的创建过程及其内部机制,为在实际开发中更好地使用 ViewModel 提供了理论基础和实践指导。