与Android和桌面项目共享一个Kotlin模块

我有一个使用LibGDX游戏框架的游戏。 目前我所针对的平台是Desktop(PC,Mac,Linux),通过独立于平台的jar和Android。

该项目托管在https://github.com/NoxHarmonium/project-whiplash如果您需要,请随时查看。

大部分代码位于一个名为core的模块中,完全由Kotlin编写。 该模块已链接到桌面和Android项目中。

这适用于Android版本7.1+和桌面。 对于所有其他版本的Android,我在匿名函数上得到一堆java.lang.NoClassDefFoundErrorexception:

 val objectObservable = this.observableCache.computeIfAbsent(assetRef, fun(assetRef: AssetRef): Observable { return Async.start(fun(): T { ... }).observeOn(this.eventLoopScheduler) }) 

exception示例:

java.lang.NoClassDefFoundError: com.projectwhiplash.utils.assets.LibGdxDataManager$objectMapFromYaml$objectMapObservable$1

这似乎是由于Kotlin默认的目标(1.8)与旧版Android支持的JVM级别(1.6)不兼容。 我可能是错的,但这解释了为什么最新版本的Android工作,因为它支持更高版本的JVM。

解决方案应该像强制Kotlin发射1.6版本的JVM字节码一样简单,但我似乎无法解决。 如果你直接将Kotlin编译成Android,这似乎是通过使用kotlin-android Gradle插件来处理的。 不幸的是,我不能使用这个插件的核心模块,因为它不应该有任何Android的依赖。

我尝试使用https://kotlinlang.org/docs/reference/using-gradle.html#compiler-options中提到的构建设置来覆盖JVM版本,如下所示:

compileKotlin { kotlinOptions { jvmTarget = "1.6" } }

然而,无论我把哪个Gradle文件放进去,它似乎都不起作用。事实上,当我尝试使用Intellij时,显示出“Can not resolve symbol’kotlinOptions’”错误。 Kotlin团队可能已经改变了一些东西,文档还没有更新。

我可以在Intellij模块设置中手动覆盖Kotlin设置,但每次同步gradle项目时都会覆盖Kotlin设置,这不是一个好的长期解决方案。 该项目旨在独立于IDE。

有谁知道如何设置核心模块,以最大限度地兼容老版本的Android?

我目前有最低的API级别设置为9,因为这是目前的LibGDX默认,但我愿意设置这个更高,如果太难以目标如此低的API级别。

编辑1:

我只是提取由核心模块生成的jar文件,并使用javap工具检查类文件。

我在随机类文件上运行以下命令

java -verbose foo.class

并用下面的文本输出文本

... minor version: 0 major version: 50 ...

使用这个问题Java类文件格式主要版本号列表? 我确定类文件实际上是针对JVM 1.6的。

因此,我原来的理论是错误的,还有一个原因,为什么旧​​的Android版本不能加载由Kotlin lambda生成的类。

看起来您正在使用只存在于JDK 8库中的function。 特别是Map类中的computeIfAbsent()方法。

因此,即使您的代码已经被编译为JVM 1.6兼容性,但Android设备上的底层实现缺少该function,因此您看到了NoClassDefFoundErrorexception的原因。

更新:您可以在位于https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#computeIfAbsent-K-java.util.function.Function的javadoc中看到computeIfAbsent()从JDK 8开始就已经存在了