如何在 Android 和 JVM 目标之间共享 Java 代码(使用 Kotlin Multiplatform)?

Posted

技术标签:

【中文标题】如何在 Android 和 JVM 目标之间共享 Java 代码(使用 Kotlin Multiplatform)?【英文标题】:How to share Java code between Android and JVM targets (with Kotlin Multiplatform)? 【发布时间】:2020-04-16 03:23:30 【问题描述】:

我正在尝试使用 Kotlin 多平台功能在 android 和 JVM 目标之间共享 Java 代码(示例项目:https://github.com/dmitrykolesnikovich/accessJavaCode-issue)

简单地说,“:library1”和“:library2”都是针对 JVM 和 Android 的 Kotlin 多平台库。 “:library2”依赖于“:library1”。他们都使用 Kotlin 和 Java。 ":library2" 旨在成为 1) Android 应用程序和 2) 桌面 (JavaFX) 应用程序的依赖项。这就是为什么 1)AAR 工件和 2)JAR 工件都需要(?) - 所以我使用 1)Android 目标和 2)JVM 目标用于“:library1”和“:library2”。

问题是,当我在 ":library1" 中有 Java 代码时

public class JavaCode  // JavaCode.java

“:library2”中的 Kotlin 代码依赖于“:library1”

class AccessJavaCode : JavaCode() // AccessJavaCode.kt

Android 目标可以识别 Java,但 JVM 目标不能:

> Task :library2:compileKotlinJvm FAILED
e: AccessJavaCode.kt: (3, 38): Unresolved reference: JavaCode

在 gradle config 中我定义了两个插件:kotlin-multiplatformcom.android.library

apply plugin: "kotlin-multiplatform"
apply plugin: "com.android.library"

kotlin 
    targets 
        jvm()
        android()
    
    sourceSets 
        jvmMain 
            dependencies 
                api kotlin("stdlib-common")
                api kotlin("stdlib-jdk8")            
            
        
        androidMain 
            dependsOn jvmMain
        
    


android 
    compileSdkVersion 28
    sourceSets 
        main 
            java.srcDirs += "src/jvmMain/kotlin" // Android target recognizes Java with this
            manifest.srcFile "src/androidMain/AndroidManifest.xml"
        
    

我很确定我的 gradle 文件很简单。非常感谢你们的帮助。

【问题讨论】:

库 1 是纯 java 库 2 是 android 库? ":library1" 和 ":library2" 都是针对 JVM 和 Android 的 Kotlin 多平台库。他们都使用 Kotlin 和 Java。 ":library2" 旨在成为 1) Android 应用程序和 2) 桌面 (JavaFX) 应用程序的依赖项。这就是为什么 1)AAR 工件和 2)JAR 工件都需要(?) - 所以我使用 1)Android 目标和 2)JVM 目标用于 ":library1" 和 ":library2"。 好的,但我的问题是这些库是使用 android 还是 java fx 框架,还是它们只是使用标准的 java 代码操作。您是否打算让其中一个库创建 Fragment 或类似的东西? 请帮忙:blog.gradle.org/alignment-with-gradle-module-metadata @DmitryKolesnikovich 不确定这是否有帮助:discuss.kotlinlang.org/t/… 【参考方案1】:

~~~~编辑~~~~

在不失去为library1 生成android 存档.aar 的能力的情况下,另一个解决方法是根据原始library1 的拆分版本的预编译人工制品制作同一库的新版本。

所以你最终会得到一个多模块的 gradle 项目,如下所示:

library1-jvm 启用了 java 插件 library1-android 启用了 android 插件 library1 将依赖于预构建的 library1-jvm.jar 和 library1-android.aar

您可以使用任何您喜欢的方式发布这些人工制品,但local maven repo 应该可以正常工作!

这意味着替换:

kotlin 
    targets 
        jvm()
        android()
    
    sourceSets 
        jvmMain 
            dependencies 
                 api kotlin("stdlib-common")
                 api kotlin("stdlib-jdk8")
            
        
        androidMain 
            dependsOn jvmMain
        
    

与:

kotlin 
    targets 
        jvm()
        android()
    
    sourceSets 
        jvmMain 
            dependencies 
                api kotlin("stdlib-common")
                api kotlin("stdlib-jdk8")
                api "com.company:library-jvm:1.0.0" 
            
        
        androidMain 
            dependsOn jvmMain
            dependencies 
                api "com.company:library-android:1.0.0"
            
        
    

这样,您在最终的library1 中根本不需要 java 插件,因为所有 java 代码都已经在单独的步骤中构建。

因此library1 可以同时保留JVMAndroid 目标

~~~~~~~~~~~~~

为了解决您的问题,您需要:

拆分您的 build.gradle 配置以便每个库有一个配置,这是必需的,因为您不能同时为同一个 Gradle 项目启用 java 插件和 android,否则您将得到以下结果Error: The 'java' plugin has been applied, but it is not compatible with the Android plugins 如果您希望 JVM 目标识别您的 Java 源文件,请在您的 library1 项目中启用 java 插件。 Java 源文件需要放在kotlin 源根目录的兄弟目录java 中。

更多信息:java-support-in-jvm-targets 的 Kotlin 文档

我还创建了一个pull request 来解决您的问题。

这种方法的缺点是您将无法为library1 生成android 存档.aar,但我想在您的android 项目中使用java 存档.jar 应该不成问题.

library1/build.gradle:

apply plugin: "kotlin-multiplatform"

kotlin 
    jvm 
        withJava()
    

    sourceSets 
        jvmMain 
            dependencies 
                api kotlin("stdlib-common")
                api kotlin("stdlib-jdk8")
            
        
    

library2/build.gradle:

apply plugin: "kotlin-multiplatform"
apply plugin: "com.android.library"

kotlin
    jvm()
    android()

    sourceSets 
        jvmMain.dependencies 
            api project(":library1")
        
        androidMain 
            dependsOn jvmMain
        
    


android 
    compileSdkVersion 28
    sourceSets 
        main 
             java.srcDirs += "src/jvmMain/kotlin"
             manifest.srcFile "src/androidMain/AndroidManifest.xml"
        
    

【讨论】:

但是如果 ":library1" 必须有 android 特定的类呢?有什么办法可以解决吗? (您的解决方案是我自己认为的,但这对我来说并不理想,所以我决定问一下) 我不明白您如何让library1 包含一些特定于android 的代码,并期望导出的.jar 在任何JVM 上运行。这是一个破碎的架构...... 我在androidMain 源集中有android 特定代码。这就是导出的.jar 不会有任何android 特定代码的方式。 好的,我明白了,但不幸的是,截至目前kotlin 1.3.61,没有办法将jvm withJavaandroid 目标放在同一个kotlin-multiplatform 项目中。你最终会得到Error: The 'java' plugin has been applied, but it is not compatible with the Android plugins 让我们continue this discussion in chat。

以上是关于如何在 Android 和 JVM 目标之间共享 Java 代码(使用 Kotlin Multiplatform)?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Eclipse Swing/maven 和 Androidstudio/gradle 项目之间共享代码?

两个 JVM 之间的共享内存

如何在主机和来宾机器之间的共享 nfs 目录中映射符号链接目标路径?

如何使用 Kotlin 同时定位 JVM/Native 和 Android

Android:如何在使用相同证书签名的项目之间共享代码

如何在 Main Target 和 UITest runner target 之间共享数据?