Jetpack系列-ViewModel的使用及原理浅析_viewmodel by,2024年最新retrofit教程

void ensureViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
// 这里会尝获取配置变更前保存的实例,这是ViewModel在配置变更后仍能保持数据的关键
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}

默认工厂和及构造参数

defaultFactory, defaultCreationExtras用于提供默认的ViewModelProvider.FactoryCreationExtras

internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
if (owner is HasDefaultViewModelProviderFactory)
owner.defaultViewModelProviderFactory
else instance

internal fun defaultCreationExtras(owner: ViewModelStoreOwner): CreationExtras =
if (owner is HasDefaultViewModelProviderFactory) {
owner.defaultViewModelCreationExtras
else CreationExtras.Empty

可以看到,两个方法首先都尝试将ViewModelStoreOwner实例转为HasDefaultViewModelProviderFactory,然后从中获取对应的默认值。如果没获取到,则返回ViewModelProvider自己提供的默认值。

先来看下ViewModelProvider提供的默认值:

public open class NewInstanceFactory : Factory {
override fun create(modelClass: Class): T {
return try {
modelClass.newInstance()
} catch (…) {

}
}
public companion object {
private var sInstance: NewInstanceFactory? = null
public val instance: NewInstanceFactory get() {
if (sInstance == null) sInstance = NewInstanceFactory()
return sInstance!!
}
}
}

可以看到,这个工厂通过直接调用Class的newInstance方法直接创建实例,这种情况下ViewModel必需要提供无参构造函数。

接下来我们看HasDefaultViewModelProviderFactory,这也是一个接口,里面声明了getDefaultViewModelProviderFactorygetDefaultViewModelCreationExtras两个方法,分别用于获取默认的工厂实例与默认的构造参数。androidx中的ComponentActivityFragment也实现了这个接口,以ComponentActivity中的实现为例:

public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}

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

ComponentActivity会提供一个SavedStateViewModelFactory实例,并且会提供一个预置了一些内容的CreationExtras实例,里面有Application实例、SavedStateRegistryOwner 的实例、ViewModelStoreOwner的实例,以及Intent中extras参数bundle。

然后是SavedStateViewModelFactory,我们直接看create方法:

override fun create(modelClass: Class, 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]
val isAndroidViewModel = AndroidViewModel::class.java.isAssignableFrom(modelClass)
val constructor: Constructor? = if (isAndroidViewModel && application != null) {
findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE)
} else {
findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE)
}
// doesn’t need SavedStateHandle
if (constructor == null) {
return factory.create(modelClass, extras)
}
val viewModel = if (isAndroidViewModel && application != null) {
newInstance(modelClass, constructor, application, extras.createSavedStateHandle())
} else {
newInstance(modelClass, constructor, extras.createSavedStateHandle())
}
viewModel
} else {
// 这里是为了兼容旧版本

}
}

除开旧版本的兼容逻辑,上面的代码根据是否使用SavedStateHandle分为两类:当不使用SavedStateHandle时,将ViewModel的构造请求发送给内部的AndroidViewModelFactory实例来处理;当使用SavedStateHandle时,则自己调用createSavedStateHandle方法创建SavedStateHandle实例,然后创建对应的ViewModel实例。关于SavedStateHandle的分析见后文。

获取ViewModel

回到开始,我们通过调用ViewModelProvider实例的get方法来获取ViewModel实例:

public open operator fun get(modelClass: Class): T {
val canonicalName = modelClass.canonicalName
?: throw IllegalArgumentException(“Local and anonymous classes can not be ViewModels”)
return get(“ V i e w M o d e l P r o v i d e r . A n d r o i d V i e w M o d e l F a c t o r y . D E F A U L T K E Y : {ViewModelProvider.AndroidViewModelFactory.DEFAULT_KEY}: ViewModelProvider.AndroidViewModelFactory.DEFAULTKEY:canonicalName”, modelClass)
}

public open operator fun get(key: String, modelClass: Class): T {
val viewModel = store[key]
if (modelClass.isInstance(viewModel)) {
(factory as? ViewModelProvider.OnRequeryFactory)?.onRequery(viewModel)
return viewModel as T
} else {
if (viewModel != null) { }
}
val extras = MutableCreationExtras(defaultCreationExtras)
extras[ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY] = key
return try {
factory.create(modelClass, extras)
} catch (e: AbstractMethodError) {
factory.create(modelClass)
}.also { store.put(key, it) }
}

获取实例需要两个参数:key和要获取的ViewModel所属类的Class对象,ViewModelProvider会从ViewModelStore中根据key查找是否有现成的实例,有就直接使用,没有就调用Factory的create创建一个。

生命周期管理的实现

ViewModel的作用域会被限定为实例化时使用的ViewModelStoreOwnerViewModelStoreOwner结束生命周期时,ViewModel就会自动回调onCleared方法用于清理依赖生命周期的工作或者对象。

class MyViewModel(
private val coroutineScope: CoroutineScope =
CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
) : ViewModel() {
override fun onCleared() {
coroutineScope.cancel()
}
}

在2.5及更高版本的lifecycle库中,ViewModel提供了更多的支持:

  • ViewModel可以接受多个Closeable对象,ViewModel会在清除时自动调用这些对象的close方法。
  • ViewModel提供了addCloseablesetTagIfAbsent等方法,这些方法允许在任意时刻添加Closeable对象到ViewModel中,这些对象同样会被自动清除。

下面还是以ComponentActivity为例看一下清理过程的实现:

public ComponentActivity() {
Lifecycle lifecycle = getLifecycle ();

getLifecycle().addObserver(new LifecycleEventObserver () {
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
// Clear out the available context
mContextAwareHelper.clearAvailableContext();
// And clear the ViewModelStore
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
}

ComponentActivity会在构造时设置一个lifecycle监听,当activity onDestroy且并非配置改变引起的调用时,执行ViewModelStore的clear方法清空所有的ViewModel,在清空前,会调用每个ViewModel的clear方法。

// ViewModelStore
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
// ViewModel
final void clear() {
mCleared = true;
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
closeWithRuntimeException(value);
}
}
}
// We need the same null check here
if (mCloseables != null) {
synchronized (mCloseables) {
for (Closeable closeable : mCloseables) {
closeWithRuntimeException(closeable);
}
}
}
onCleared();
}

在ViewModel的clear方法中,对所有保存的Closeable执行close,然后调用onCleared

再看看ktx中的viewModelScope

public 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.immediate)
)
}

internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context

override fun close() {
coroutineContext.cancel()
}
}

其本质也是提供了一个实现Closeable接口的CoroutineScope,然后通过setTagIfAbsent设置给ViewModel。

配置变更后仍保持数据的原理

前面在分析ViewModelStore的获取时,我们知道ComponentActivity在初始化ViewModelStore时,会先调用getLastNonConfigurationInstance,尝试恢复配置未变更前保存的ViewModelStore。与之对应的也有配置变更时保存ViewModelStore的逻辑:

public final Object onRetainNonConfigurationInstance() {
// Maintain backward compatibility.
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
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;
}

onRetainNonConfigurationInstance会在Activity配置发生变更(如横竖屏切换)需要重建时,它会将返回的Object直接保存到ActivityClientRecord中:

// ActivityThread
void performDestroyActivity(ActivityClientRecord r, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason
) {

if (getNonConfigInstance) {
try {
r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
} catch (Exception e) {

}
}

}

Activity重建时再设置回去:

// ActivityThread
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
r.assistToken, r.shareableActivityToken);

}
// Activity
final void attach(Context context, ActivityThread aThread,

NonConfigurationInstances lastNonConfigurationInstances,

) {

mLastNonConfigurationInstances = lastNonConfigurationInstances;

}

既然ViewModelStore实例在重建时被保存和恢复了,那么其中的ViewModel及其状态数据也自然不会变化。

SavedStateHandle实现原理

前面我们在分析ComponentActivity中提供的默认工厂SavedStateViewModelFactory时,提到了工厂会在需要使用SavedStateHandle调用createSavedStateHandle创建实例:

public fun CreationExtras.createSavedStateHandle(): SavedStateHandle {
val savedStateRegistryOwner = this[SAVED_STATE_REGISTRY_OWNER_KEY]
?: throw IllegalArgumentException(…)
val viewModelStateRegistryOwner = this[VIEW_MODEL_STORE_OWNER_KEY]
?: throw IllegalArgumentException(…)
val defaultArgs = this[DEFAULT_ARGS_KEY]
val key = this[ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY]
?: throw IllegalArgumentException(…)
return createSavedStateHandle(
savedStateRegistryOwner, viewModelStateRegistryOwner, key, defaultArgs
)
}

这里会从CreationExtras中获取一些必要参数:

  • savedStateRegistryOwnerSavedStateRegistryOwner是jetpack-savedstate库中的一个接口,实现此接口的类表明可以在应用意外销毁时支持保存/恢复状态。jetpack中的ComponentActivityFragment实现了它。
  • viewModelStateRegistryOwner:这里实际获取的是当前的ViewModelStoreOwner,工厂会将创建出来的SavedStateHandle实例保存在一个专门的ViewModel—SavedStateHandlesVM中以加快访问。
  • defaultArgs:传递给SavedStateHandle的默认参数。
  • key:与待创建ViewModel相关联的键,用于从SavedStateHandlesVM中存取SavedStateHandle实例。

private fun createSavedStateHandle(
savedStateRegistryOwner: SavedStateRegistryOwner,
viewModelStoreOwner: ViewModelStoreOwner,
key: String, defaultArgs: Bundle?
): SavedStateHandle {
val provider = savedStateRegistryOwner.savedStateHandlesProvider
val viewModel = viewModelStoreOwner.savedStateHandlesVM
// If we already have a reference to a previously created SavedStateHandle
// for a given key stored in our ViewModel, use that. Otherwise, create
// a new SavedStateHandle, providing it any restored state we might have saved

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

[外链图片转存中…(img-kAh2SBn5-1712682028564)]
[外链图片转存中…(img-NrSMsu0Y-1712682028564)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
[外链图片转存中…(img-zfb0jCni-1712682028564)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值