【问题标题】:Share a Kotlin module with an Android and Desktop project与 Android 和桌面项目共享 Kotlin 模块
【发布时间】:2017-05-21 11:10:07
【问题描述】:

我正在开发一款使用 LibGDX 游戏框架的游戏。目前我的目标平台是通过独立于平台的 jar 和 Android 的桌面(PC、Mac、Linux)。

该项目托管在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 像 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 显示“无法解析符号 'kotlinOptions'”错误。可能是 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 生成的类。

【问题讨论】:

    标签: android intellij-idea gradle libgdx kotlin


    【解决方案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 之后才存在

    【讨论】:

    • 嗨,谢谢你的想法。如果您检查我对原始答案的编辑,您会发现我发现 Kotlin 实际上正在生成针对 JVM 1.6 的二进制文件。在 1.1.2 的更改日志中,他们说“对于编译器生成的代码,Java 1.6 兼容性仍然是默认设置,我们没有计划放弃对生成 Java 1.6 兼容字节码的支持。” Source所以肯定是另一个问题!
    • 好皮卡!我以为我检查过了,但我一定错过了。我会看看我是否可以用 polyfill 或替换之类的东西来修复它。
    • 删除对仅在 JVM 1.7 和 1.8 中支持的方法的任何引用有效。出于某种原因,Intellij 只警告了某些地方不支持的方法,而不是其他地方。澄清一下,这不是 Kotlin 发出的字节码版本,而是我使用最新版本的 JDK(1.8)来构建我的代码。这意味着它可以编译使用旧 Android 设备上提供的早期版本 JVM 中不可用的方法的类。
    • 您想编辑您的答案以便我接受还是应该发布另一个答案?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多