使用 Shadow 重定位的包中的 Kotlin 属性不起作用

Kotlin properties in a package relocated with Shadow do not work

我正在尝试使用 Shadow 重新定位一个包(具体来说是 OkHttp 4),使用以下 Gradle 配置:

apply plugin: 'java'
apply plugin: 'com.github.johnrengelman.shadow'

shadowJar {
    archiveBaseName.set('my_archive')
    archiveClassifier.set(null)
    version = null

    relocate 'okhttp3', 'my.prefix.okhttp3'
    relocate 'okio', 'my.prefix.okio'
}

dependencies {
    implementation("com.squareup.okhttp3:okhttp:4.2.1") {
        exclude group: "org.jetbrains.kotlin", module: "kotlin-stdlib"
    }
}

(我省略了 buildscript 部分,重要的一点是使用的 Shadow 版本是 5.1.0。包前缀等也已更改)

这在 OkHttp 3.12.0 及更早版本之前有效,纯粹是 Java。现在 OkHttp 4 是用 Kotlin 编写的,我在使用属性时遇到了问题,特别是在 Kotlin 代码中。从 Java 使用时,重新定位的 OkHttp 工作正常。但是在 Kotlin 中访问属性,就像这样:

val cache = httpClient.cache

...导致异常:

java.lang.NoSuchMethodError: No virtual method getCache()Lmy/prefix/okhttp3/Cache; in class Lmy/prefix/okhttp3/OkHttpClient; or its super classes (declaration of 'my.prefix.okhttp3.OkHttpClient' appears in /data/app/redacted.redacted-0yalPGR5aw0RSY2Zdxnq7Q==/base.apk)

如您所见,该应用是一个 Android 应用,以防万一。

知道我的配置有什么问题吗?

我们在非 android 项目中经历过类似的事情。 包级 "things"(在我们的例子中是一个扩展函数)实际上在编译器生成的 class 上是静态的(在我们的例子中它称为 ExtensionKt.class)

我从 Java 或 Scala 代码访问 class 没有问题。 然而,在 Kotlin 代码中,编译器不允许我访问 ExtensionKt class 并且由 shadow Jar 重定位的包以某种方式破坏了原始语法,因此无法再通过包名称访问它。作为临时解决方法,我们从 Java:

访问包范围内的东西

因此改为使用此 Kotlin 代码:

import my.package.name.niceExtensionFunction

...

val x = SomeClass()
val y = x.niceExtensionFunction()

我们在 Java 中这样做:

import my.package.name.ExtensionKt;

...

SomeClass x = new SomeClass;
Object y = ExtensionKt.niceExtensionFunction(x)

这不是一个完美的解决方案,但至少可以让事情在一定程度上奏效。与其完全排除嵌入式 Kotlin stdlib,不如顺其自然并重新定位它。最简单的方法是将 everything 重定位到相同的前缀,将其添加到 build.gradle:

task relocateShadowJar(type: ConfigureShadowRelocation) {
    target = tasks.shadowJar
    prefix = "my.prefix"

}

tasks.shadowJar.dependsOn tasks.relocateShadowJar

您可能还需要在 shadowJar 任务中排除一些 Kotlin META-INF 位。

这种方法的问题(至少对我而言)是,虽然一切或多或少都有效,但您现在可能在您的应用程序中有两次 Kotlin stdlib:您的前缀版本和您通常使用的版本。此外,属性 语法不起作用。所以在问题的例子中,

val cache = httpClient.cache

实际上变成了

val cache = httpClient.cache()

...这不是很好。

我现在的另一个问题是,在 Android Studio 中,生成的 jar 中的所有内容在代码编辑器中都显示为红色,即 Android Studio 看不到其中的内容jar。一切都编译得很好,只是编辑代码本身很痛苦,因为您没有完成任何代码并且 Android Studio 有时会弄乱您的导入。 (因为它显然认为这些导入是无效的)