为何Android的TypedArray存在自定义use扩展函数实现?
背景
Android中的TypedArray使用完毕后必须调用recycle()方法回收资源,避免内存泄漏。它实现了AutoCloseable接口,因此理论上可以直接使用Kotlin标准库的.use { ... }函数自动完成回收操作。
Kotlin标准库的use函数实现如下:
@Suppress("ACTUAL_WITHOUT_EXPECT") @SinceKotlin("1.2") @kotlin.internal.InlineOnly // TODO: remove java.lang package prefix when the kotlin.AutoCloseable typealias is introduced and KT-55392 is fixed. // The prefix is currently needed for the current dokka to generate correct signature. public actual inline fun <T : java.lang.AutoCloseable?, R> T.use(block: (T) -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } var exception: Throwable? = null try { return block(this) } catch (e: Throwable) { exception = e throw e } finally { this.closeFinally(exception) } } @SinceKotlin("1.2") @PublishedApi internal fun java.lang.AutoCloseable?.closeFinally(cause: Throwable?) = when { this == null -> {} cause == null -> close() else -> try { close() } catch (closeException: Throwable) { cause.addSuppressed(closeException) } }
TypedArray对close()方法的重写逻辑非常简单,直接调用recycle():
public void close() { recycle(); }
但Android SDK还单独为TypedArray提供了一个同名的自定义use扩展函数:
public inline fun <R> TypedArray.use(block: (TypedArray) -> R): R { return block(this).also { recycle() } }
自定义use扩展函数的存在原因
1. 历史兼容性
TypedArray是从Android API 24开始才实现AutoCloseable接口的。在这之前,低版本的Android项目无法使用Kotlin标准库的use函数来自动回收TypedArray。这个自定义扩展函数的出现,是为了让API 24以下的项目也能通过简洁的use语法安全管理TypedArray资源。
2. 简化逻辑与微小性能优化
标准库的use函数是通用实现,需要适配所有AutoCloseable类型:
- 它会处理
block执行时抛出异常的情况,若close()也抛出异常,会将其标记为被抑制的异常; - 支持可空的
AutoCloseable?类型,因此包含空值判断逻辑。
而TypedArray的recycle()方法不会抛出任何异常(官方文档明确说明),自定义的use函数可以完全省去这些通用化的异常处理和空值检查逻辑,直接在block执行完成后调用recycle()。虽然单看一次调用性能提升不明显,但在频繁创建TypedArray的场景(比如列表Adapter的getView方法中),累计下来的开销节省是可感知的。
同时,自定义实现的代码逻辑更直观简洁,直接通过also函数完成回收操作。
3. 语义更贴合场景
标准库的use函数是针对所有资源的通用关闭逻辑,而自定义的TypedArray.use函数直接绑定recycle()操作,语义上更贴合TypedArray的资源回收场景,让代码意图更清晰。
关于同名函数的导入问题
你提到的同名函数容易导错的问题确实存在,当项目同时引入Kotlin标准库和Android SDK相关依赖时,IDE会提示多个use函数可选。可以通过以下方式规避:
- 明确指定导入路径:在代码顶部添加
import android.content.res.TypedArray.use,强制使用Android自定义版本; - 若项目仅支持API 24及以上,可直接使用标准库的
use函数,无需导入Android的扩展版本。
内容的提问来源于stack exchange,提问作者NoBullsh1t

