使用 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 属性不起作用的主要内容,如果未能解决你的问题,请参考以下文章
myeclipse 如何将一个工程中的包复制到另一个工程中的指定包中