【问题标题】:Kotlin Native compile jar and frameworkKotlin Native 编译 jar 和框架
【发布时间】:2021-03-10 09:13:17
【问题描述】:

我正在为 Android 和 iOS 构建一个多平台库。我的 gradle 文件如下所示:

plugins {
    id 'org.jetbrains.kotlin.multiplatform' version '1.4.0'
}
repositories {
    mavenCentral()
}
group 'com.example'
version '0.0.1'

apply plugin: 'maven-publish'

kotlin {
    jvm()
    // This is for iPhone simulator
    // Switch here to iosArm64 (or iosArm32) to build library for iPhone device
    ios {
        binaries {
            framework()
        }
    }
    sourceSets {
        commonMain {
            dependencies {
                implementation kotlin('stdlib-common')
                implementation("com.ionspin.kotlin:bignum:0.2.2")
            }
        }
        commonTest {
            dependencies {
                implementation kotlin('test-common')
                implementation kotlin('test-annotations-common')
            }
        }
        jvmMain {
            dependencies {
                implementation("com.ionspin.kotlin:bignum:0.2.2")
            }
        }
        jvmTest {
            dependencies {
                implementation kotlin('test')
                implementation kotlin('test-junit')
            }
        }
        iosMain {
        }
        iosTest {
        }
    }
}

configurations {
    compileClasspath
}

我正在使用第三方库,我是这样使用它的:

fun test(value: String): Int {
    return BigDecimal.parseString(value).toBigInteger().intValue()
}

问题是当我构建 .jar 时没有包含 bignum 库,当我在 Android 项目中使用该库时,我得到一个异常 ClassNotFoundException: 找不到类“com.ionspin.kotlin.bignum.十进制.BigDecimal"。

有没有办法在 Android 的 .jar 和 iOS 的 .framework 中包含第三方库?

【问题讨论】:

  • 你能把你的gradle文件分享一个github.com,那我可以帮你,最好的官方例子在这里github.com/Kotlin/kmm-sample,文档在这里kotlinlang.org/docs/mobile/samples.html,你需要使用child毕业模块
  • 问题是 Android 和 iOS 项目非常大,我不想将它们作为子 gradle 模块包含在新项目中。
  • 会不会是图书馆的出版方式?您是否也尝试向图书馆作者询问?我已经尝试过使用其他 3rd 方库,但问题确实没有发生。 This reference 也可以提供帮助。
  • 我实际上正在使用这个库github.com/korlibs/krypto 问题中的一个只是举例。 Krypto lib 也有同样的问题。

标签: android ios gradle kotlin-native


【解决方案1】:

JVM

所以,我发现生成像您期望的那样工作的 Fat JAR 的唯一方法是在您的 KMP 的 project:build.gradle.kts 中添加两个 自定义 gradle 任务应用 java 插件后的库。

plugins {
    [...]
    id("java")
}

[...]

kotlin {
    jvm {
        [...]
        compilations {
            val main = getByName("main")
            tasks {
                register<Copy>("unzip") {
                    group = "library"
                    val targetDir = File(buildDir, "3rd-libs")
                    project.delete(files(targetDir))
                    main.compileDependencyFiles.forEach {
                        println(it)
                        if (it.path.contains("com.")) {
                            from(zipTree(it))
                            into(targetDir)
                        }
                    }
                }
                register<Jar>("fatJar") {
                    group = "library"
                    manifest {
                        attributes["Implementation-Title"] = "Fat Jar"
                        attributes["Implementation-Version"] = archiveVersion
                    }
                    archiveBaseName.set("${project.name}-fat")
                    val thirdLibsDir = File(buildDir, "3rd-libs")
                    from(main.output.classesDirs, thirdLibsDir)
                    with(jar.get() as CopySpec)
                }
            }
            tasks.getByName("fatJar").dependsOn("unzip")
        }

    }
    [...]
}

然后您必须启动fatJar gradle 任务,该任务会生成一个 .jar 文件,其中包含从它们对应的 jar 档案中提取的第三个库类。

您可以进一步自定义这两个自定义 gradle 脚本,以更好地满足您的需求(这里我只包括 com. 包名起始 deps)。

然后在您的 Android 应用 app:build.gradle 文件中,您可以照常使用它,也可以简单地使用它

implementation files('libs/KMLibraryTest001-fat-1.0-SNAPSHOT.jar')

iOS

正如您还要求标题中的 iOS 部分(即使它是您问题主要主题中的第二个公民),您只需使用 api 而不是 implementation 作为您的第 3 方库以及框架的导出选项。

ios() {
    binaries {
        framework() {
            transitiveExport = true // all libraries
            //export(project(":LibA")) // this library project in a trainsitive way
            //export("your 3rd party lib") // this 3rd party lib in a transitive way
        }
    }
}

您可以找到完整的参考资料here

【讨论】:

  • 嗨,我在 .gradle 中的两次出现上都将 implementation("com.ionspin.kotlin:bignum:0.2.2") 更改为 api("com.ionspin.kotlin:bignum:0.2.2")文件,它仍然无法正常工作。我正在使用 kotlin 多平台 1.4.20 和 gradle 包装器版本 6.7.1。我尝试使用 IntelliJ Run -> Build 构建 .jar,但仍然得到相同的异常 ClassNotFoundException,这意味着不包括第三方依赖项。
  • 我仍然遇到同样的错误。我使用“构建工件”方法构建 .jar,并将第 3 方 lib 元数据 jar 附加到构建的 jar 中。最终的 jar 现在比以前大了,但是当我尝试将它包含到另一个 Android 项目中时(实现 fileTree(dir: 'libs', include: ['*.jar']))我仍然得到 ClassNotFoundException。
  • @box 上面的 cmets 已经过时了 ;)
  • 我接受了你的回答。但是还没有成功,我找不到 Copy 方法。
  • @box 您的库使用哪个模板?我想您将 IntelliJ IDEA 与 kotlin 多平台库模板一起使用。如果您包含 java 插件并复制和粘贴我的脚本,则此模板开箱即用。
【解决方案2】:

如果你看到Krypto library,它有

androidMain
jsMain
jvmMain
mingwX64Main
nativPosixMain

这意味着生成了 5 种二进制文件以支持 5 个平台
令人信服地,这解释了每个平台都需要自己的二进制文件

例如,

windows -- DLL 文件
linux -- so 文件
java -- JAR文件
mac -- dylib 文件

JAR 被加载到 JVM,但 IOS 不使用 JVM
分离具有共同逻辑的实用程序函数并编写 gradle 以针对多个平台

如果你想入手纯多平台,可以试试这个Official Example

或者创建一个子gradle模块,创建一个IOS和Android通用的库项目

可能的目标已正确记录here

我创建了一个应用程序,它将二进制文件发布到本地存储库并在MainActivity 中重复使用——您可以获取代码here

修改local.properties为android SDK位置并使用

gradlew assemble

构建 APK 并自行测试

打开mylib\build.gradle.kts文件夹,可以看到目标jvmiosX64jvm用于android

【讨论】:

    【解决方案3】:

    如果我正确使用 api 而不是 implementation 应该可以解决您的问题,尽管我还没有在 Native 部分尝试过

    Api and implementation separation

    【讨论】:

      猜你喜欢
      • 2020-03-15
      • 2018-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-22
      • 2011-02-24
      相关资源
      最近更新 更多