Android 面试总结 - viewModelScope 什么时候关闭的?,这些年我所经历的所有面试

本文分析了ViewModel中的onCleared方法如何与Closeable接口结合,确保当ViewModel被清除时,与其关联的CoroutineScope会正确关闭。主要讨论了mBagOfTagsMap的作用和clear()方法中closeWithRuntimeException的调用逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

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

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

作者2013年从java开发,转做Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

参与过不少面试,也当面试官 面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我整理了一份阿里P7级别的最系统的Android开发主流技术,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括阿里,以及字节跳动,腾讯,华为,小米,等一线互联网公司主流架构技术。如果你想深入系统学习Android开发,成为一名合格的高级工程师,可以收藏一下这些Android进阶技术选型

我搜集整理过这几年阿里,以及腾讯,字节跳动,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

Java语言与原理;
大厂,小厂。Android面试先看你熟不熟悉Java语言

高级UI与自定义view;
自定义view,Android开发的基本功。

性能调优;
数据结构算法,设计模式。都是这里面的关键基础和重点需要熟练的。

NDK开发;
未来的方向,高薪必会。

前沿技术;
组件化,热升级,热修复,框架设计

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

我在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多,CodeChina上可见;

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

存中…(img-jrI5Zgw0-1712366118974)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

我在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多,CodeChina上可见;

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值