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

Posted

技术标签:

【中文标题】使用 Shadow 重定位的包中的 Kotlin 属性不起作用【英文标题】:Kotlin properties in a package relocated with Shadow do not work 【发布时间】:2020-04-05 19:31:49 【问题描述】:

我正在尝试使用 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 应用,以防万一。

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

【问题讨论】:

【参考方案1】:

我们在非安卓项目中遇到了类似的情况。 包级别的“事物”(在我们的例子中是扩展函数)实际上在编译器生成的类(在我们的例子中称为 ExtensionKt.class)上是静态的

从 Java 或 Scala 代码访问该类没有问题。 然而,在 Kotlin 代码中,编译器不允许我访问 ExtensionKt 类,并且由影子 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)

【讨论】:

【参考方案2】:

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

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



tasks.shadowJar.dependsOn tasks.relocateShadowJar

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

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

val cache = httpClient.cache

其实变成了

val cache = httpClient.cache()

...这不是很好。

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

【讨论】:

以上是关于使用 Shadow 重定位的包中的 Kotlin 属性不起作用的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin的包

myeclipse 如何将一个工程中的包复制到另一个工程中的指定包中

从Kotlin构造函数android中的Parcelable读取List列表

UML:包中的包

shadow-root中的元素定位方法

shadow-root中的元素定位方法