Search code examples
androidkotlin

Why is there a custom impl of `use` for Android's `TypedArray`?


Android's TypedArrays need to be .recycle()d. Also, TypedArray implements AutoClosable, which means that we can use Kotlin's .use { ... } construct to automate closing/recycling. That is implemented as

@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)
        }
}

and TypedArray's override of the .close method is simply

public void close() {
  recycle();
}

but there is also a custom extension function, equally named .use, just for TypedArray

public inline fun <R> TypedArray.use(block: (TypedArray) -> R): R {
    return block(this).also {
        recycle()
    }
}

I wonder why that extension function exists. It is arguably much simpler, but I kinda doubt it'll make much of a difference. Are there any reasons other than performance to create this extension function? Or is the performance gain actually significant?

I also find it a somewhat poor choice to name the two functions identically, because it's now really easy to occasionally import the wrong one!


Solution

  • This custom version of use is a legacy from old versions of the Android API.

    The TypedArray class implements AutoCloseable, but it existed before the Kotlin stdlib had a use extension function for AutoCloseable. That came with Kotlin 1.2, and only when using the jdk7 version of the JVM target (at the time, Kotlin also supported Java 1.6). At the time, the use extension was only defined for the Closeable interface, which TypedArray does not implement.

    Note that this custom definition of use has different behaviour than Kotlin's AutoCloseable.use, as it doesn't recycle if the code throws an exception. Judging from this conversation, it appears that it wasn't considered important for it to recycle if an exception was thrown.

    You don't have to use this custom definition. If you don't import it, your code will use the AutoCloseable version from the Kotlin standard library, which will work just as well.