hlu*_*kyi 6 sqlite android kotlin
我在我的项目中使用此代码
fun getAllData(): List<Data> = writableDatabase.use { db ->
db.query(...)
}
Run Code Online (Sandbox Code Playgroud)
它在Lollipop设备上成功执行,但在Lollipop之前它会抛出ClassCastException
FATAL EXCEPTION: main
java.lang.ClassCastException: android.database.sqlite.SQLiteDatabase cannot be cast to java.io.Closeable
Run Code Online (Sandbox Code Playgroud)
可能是因为它是Java 1.6并且没有try-with-resources功能,但我不确定.
那么为什么我有这个例外以及它如何修复?
该问题与try-with-resources无关,而仅仅是编译后的代码与运行时环境之间的不兼容依赖项问题。
问题是在编译时SQLiteDatabase实现该接口Closeable,然后将其换成另一个没有实现的实现。但是代码已经编译完毕,并且不知道此更改。
调用函数/方法时,将检查所有参数以确保它们是正确的类型,这通常由JVM处理。对于内联和扩展功能,Kotlin编译器会插入类型检查以确保其在正确的类型上运行,对于此特定的内联功能,该函数的接收者定义为Closeable。这是方法签名:
inline fun <T : Closeable, R> T.use(block: (T) -> R): R { ... }
Run Code Online (Sandbox Code Playgroud)
因此,编译器内联writableDatabase如下类型检查(以bytecode表示):
CHECKCAST java/io/Closeable
Run Code Online (Sandbox Code Playgroud)
因此,在这一点上,SQLiteDatabase必须永远实现Closeable接口的实现,否则编译后的代码将失败。通过切换到不再适用的旧版Android,您将违反合同并引发异常。编译器无法做任何不同的事情。就像我在编译代码后将任何JVM应用程序中的JAR换成具有完全不同的实现的JAR一样。更改Android版本基本上会交换所有JAR。
要解决此问题,您可以做的是use,SQLiteDatabase如果所有版本都有一个close方法(从Kotlin stdlib use函数复制和修改),则专门为该类编写您自己的函数:
inline fun <T : SQLiteDatabase, R> T.use(block: (T) -> R): R {
var closed = false
try {
return block(this)
} catch (e: Exception) {
closed = true
try {
close()
} catch (closeException: Exception) {
// eat the closeException as we are already throwing the original cause
// and we don't want to mask the real exception
}
throw e
} finally {
if (!closed) {
close()
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,内联CHECKCAST android/database/sqlite/SQLiteDatabase类型检查将始终成功。
| 归档时间: |
|
| 查看次数: |
937 次 |
| 最近记录: |