おでーぶでおでーぶ

いろいろ書く。いろいろ。

Kotlinの拡張関数を使って、Cursor#use すると ClassCastException で落ちる

現象

Fatal Exception: java.lang.ClassCastException: android.content.ContentResolver$CursorWrapperInner cannot be cast to java.io.Closeable

原因

Cursor が Closeable を継承するのは API 16 から。ContentResolver の返す Cursor が custom cursor にできない問題に気を取られすぎて、Closeable を継承してないのを忘れていた・・・

明示的に Cursor を Closeable にすると、ちゃんと Android Lint が怒ってくれる。use を使う場合は出てこない。悲しみが深い。

f:id:jmatsu:20181115134434j:plain

解決策

以下の拡張関数をcompatで書いて対応。関数名をuseにしていないのは書いてるときにimportする関数を間違える可能性が高いから。

import android.database.Cursor
import android.os.Build
import kotlin.io.use as ioUse

inline fun <T : Cursor?, R> T.useCompat(block: (T) -> R): R {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        ioUse(block)
    } else {
        var exception: Throwable? = null
        try {
            return block(this)
        } catch (expected: Throwable) {
            exception = expected
            throw expected
        } finally {
            when {
                this == null -> {
                }
                exception == null -> close()
                else ->
                    try {
                        close()
                    } catch (ignored: Throwable) {
                    }
            }
        }
    }
}

理想的な解決策

minimum support API を28にする