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

How to share Java code between Android and JVM targets (with Kotlin Multiplatform)?

我正在尝试使用 Kotlin Multiplatform 功能在 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配置中我定义了两个插件: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 文件很简单。非常感谢你们的帮助。

~~~~编辑~~~~

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

的拆分版本

所以你最终会得到一个多模块 gradle 项目,就像这样:

  • 启用了 java 插件的 library1-jvm
  • 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 配置以便每个库有一个配置,这是必需的,因为您不能同时启用 java 插件和 android同一个 Gradle 项目,否则你将得到以下 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,但我想使用 java 存档.jar 在你的 android 项目中应该完全不是问题。

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"
        }
    }
}