我正在开发一款使用LibGDX游戏框架的游戏。目前我所定位的平台是桌面(PC,Mac,Linux),通过独立于平台的jar和Android。
项目在https://github.com/NoxHarmonium/project-whiplash举办。如果需要,请随时查看。
大部分代码都在一个名为core
的模块中,完全用Kotlin编写。此模块链接到桌面和Android项目。
这适用于Android版本7.1+和桌面版。对于所有其他版本的Android,我在匿名函数上获得了大量java.lang.NoClassDefFoundError
例外,例如:
val objectObservable = this.observableCache.computeIfAbsent(assetRef, fun(assetRef: AssetRef): Observable<T> {
return Async.start(fun(): T {
...
}).observeOn(this.eventLoopScheduler)
})
例外示例:
java.lang.NoClassDefFoundError: com.projectwhiplash.utils.assets.LibGdxDataManager$objectMapFromYaml$objectMapObservable$1
这似乎是由于默认情况下Kotlin所针对的JVM不兼容(1.8)以及旧版Android支持的JVM级别(1.6)。我可能错了,但这解释了为什么最新版本的Android工作,因为它支持更高版本的JVM。
解决方案应该像强制Kotlin发布JVM字节代码一样简单,因为版本1.6但我似乎无法解决它。如果您将Kotlin直接编译到Android中,这似乎可以通过使用kotlin-android
Gradle插件来处理。不幸的是,我不能将此插件用于核心模块,因为它不应该有任何Android依赖项。
我尝试使用https://kotlinlang.org/docs/reference/using-gradle.html#compiler-options中提到的构建设置覆盖JVM版本,如下所示:
compileKotlin {
kotlinOptions {
jvmTarget = "1.6"
}
}
然而,无论我把它放在哪个Gradle文件中它似乎都不起作用。实际上我得到了一个&#34;无法解析符号&#39; kotlinOptions&#39;&#34;我尝试时Intellij显示错误。 Kotlin团队可能已经改变了一些内容并且文档尚未更新。
我可以在Intellij模块设置中手动覆盖Kotlin设置,但每次同步gradle项目时它都会被覆盖,这不是一个好的长期解决方案。该项目旨在独立于IDE。
有谁知道如何设置核心模块以最大限度地兼容旧版Android?
我目前将最低API级别设置为9,因为这是当前的LibGDX默认值,但如果要将这么低的API级别定位太难,我愿意将其设置得更高。
编辑1:
我刚刚提取了核心模块生成的jar文件,并使用javap
工具检查了类文件。
我在随机类文件
上运行以下命令 java -verbose foo.class
并输出带有以下文字的文字
...
minor version: 0
major version: 50
...
使用此问题List of Java class file format major version numbers?我确定类文件实际上是针对JVM 1.6。
因此我的原始理论是错误的,并且还有另一个原因导致旧的Android版本无法加载Kotlin lambdas生成的类。
答案 0 :(得分:1)
看起来您正在使用仅存在于JDK 8库中的功能。特别是Map类上的computeIfAbsent()方法。
正因为如此,即使您的代码已经编译为JVM 1.6兼容性,Android设备上的底层实现也缺少该功能,因此您看到了NoClassDefFoundError异常的原因。
更新:您可以在位于https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#computeIfAbsent-K-java.util.function.Function-的javadoc中看到computeIfAbsent()仅在JDK 8之后出现