closeWithRuntimeException(value);
}
}
}
onCleared();
}
@SuppressWarnings(“unchecked”)
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 getTag(String key) {
if (mBagOfTags == null) {
return null;
}
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);
}
}
}
}
onCleared() 方法是空方法??? 说好的 “在 ViewModel 被清除,即调用[ViewModel.onCleared] 时,将取消此作用域。” 呢? 没有代码怎么取消?
看 ViewModel 的源码发现 onCleared() 被 clear() 调用了,那我们来看看 clear() 做了什么:
@MainThread
final void clear() {
// 标记当前 ViewModel 已经被清除
mCleared = true;
// 判断 mBagOfTags 是否为空,不为空则遍历 map 的 value 并且调用了 closeWithRuntimeException() 方法
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
// see comment for the similar call in setTagIfAbsent
closeWithRuntimeException(value);
}
}
}
onCleared();
}
我们再看看 mBagOfTags 是干什么的:
@Nullable
private final Map<String, Object> mBagOfTags = new HashMap<>();
它是一个 Map ,找一下什么时候调用 mBagOfTags.put() :
/**
-
设置与此viewmodel关联的标记和键。
-
Sets a tag associated with this viewmodel and a key.
-
如果给定的 newValue 是 Closeable,一旦 clear(),它就会关闭。
-
If the given {@code newValue} is {@link Closeable},
-
it will be closed once {@link #clear()}.
-
-
如果已经为给定的键设置了一个值,则此调用不执行任何操作,并且返回当前关联的值,给定的 newValue 将被忽略
-
If a value was already set for the given key, this calls do nothing and
-
returns currently associated value, the given {@code newValue} would be ignored
-
-
如果ViewModel已经被清除,那么将对返回的对象调用close(),如果它实现了 closeable。同一个对象可能会收到多个close调用,因此方法应该是幂等的。
-
If the ViewModel was already cleared then close() would be called on the returned object if
-
it implements {@link Closeable}. The same object may receive multiple close calls, so method
-
should be idempotent.
*/
@SuppressWarnings(“unchecked”)
T setTagIfAbsent(String key, T newValue) {
// 原值
T previous;
synchronized (mBagOfTags) {
// 尝试获取是否命中 key
previous = (T) mBagOfTags.get(key);
if (previous == null) {
// 未命中,则 put 到 mBagOfTags 中
mBagOfTags.put(key, newValue);
}
}
// 返回值赋值
T result = previous == null ? newValue : previous;
// 如果此 viewModel 被标记清除
if (mCleared) {
// 我们可能会在同一个对象上多次调用close(),
// 但是Closeable接口要求close方法是幂等的:“如果流已经关闭,那么调用这个方法就没有效果。”
// It is possible that we’ll call close() multiple times on the same object, but
// Closeable interface requires close method to be idempotent:
// “if the stream is already closed then invoking this method has no effect.” ©
// 调用 Closeable 的 close 方法
closeWithRuntimeException(result);
}
return result;
}
setTagIfAbsent() 方法有点熟悉?原来它在获取 viewModelScope 对象时候被调用了。
得知 viewModelScope 对象缓存在 Map 类型的对象 mBagOfTags 中。
setTagIfAbsent 的注释上说 T newValue 是实现了 Closeable 接口的。
再回顾下 viewModelScope 怎么获取的:
private const val JOB_KEY = “androidx.lifecycle.ViewModelCoroutineScope.JOB_KEY”
/**
-
与此[ViewModel]绑定的[CoroutineScope]
-
[CoroutineScope] tied to this [ViewModel].
-
清除ViewModel时,即调用[ViewModel.onCleared]时,将取消此作用域
-
This scope will be canceled when ViewModel will be cleared, i.e [ViewModel.onCleared] is called
-
This scope is bound to
-
[Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate]
*/
public val ViewModel.viewModelScope: CoroutineScope
get() {
// 先尝试从缓存中获取 tag 为 JOB_KEY 的 CoroutineScope 对象
// 若命中,则返回 viewModelScope
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
// 未命中缓存,则通过 setTagIfAbsent() 添加到 ViewModel 中
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()
}
}
viewModelScope 第一次被调用时,会调用 setTagIfAbsent(JOB_KEY,CloseableCoroutineScope) 进行缓存。
看下 CloseableCoroutineScope 类,实现了 Closeable 接口,并且在 close() 中进行了协程作用域 coroutineContext 对象的取消操作。
至此,我们知道了 **viewModelScope 对象怎么添加到 ViewModel 里
并且在 ViewModel 被清除时 viewModelScope 会被取消。**
第2个问题 为什么调用 [ViewModel.onCleared] 时,viewModelScope 会被取消。 解决!
如果感觉文章可以学到东西,欢迎大佬关注小弟的公众号:Android 翻山之路
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/526d180ff6e675d9f940a1d32eb72ead.jpeg)
最后
我见过很多技术leader在面试的时候,遇到处于迷茫期的大龄程序员,比面试官年龄都大。这些人有一些共同特征:可能工作了5、6年,还是每天重复给业务部门写代码,工作内容的重复性比较高,没有什么技术含量的工作。问到这些人的职业规划时,他们也没有太多想法。
其实30岁到40岁是一个人职业发展的黄金阶段,一定要在业务范围内的扩张,技术广度和深度提升上有自己的计划,才有助于在职业发展上有持续的发展路径,而不至于停滞不前。
不断奔跑,你就知道学习的意义所在!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
,还是每天重复给业务部门写代码,工作内容的重复性比较高,没有什么技术含量的工作。问到这些人的职业规划时,他们也没有太多想法。
其实30岁到40岁是一个人职业发展的黄金阶段,一定要在业务范围内的扩张,技术广度和深度提升上有自己的计划,才有助于在职业发展上有持续的发展路径,而不至于停滞不前。
不断奔跑,你就知道学习的意义所在!
[外链图片转存中…(img-y3VjWH3C-1712691233706)]
[外链图片转存中…(img-7h0Xw0JI-1712691233707)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!