【问题标题】:How to resolve a library conflict (apache commons-codec)如何解决库冲突(apache commons-codec)
【发布时间】:2012-08-30 10:04:04
【问题描述】:

我对 Android 库有疑问。

我想使用库 org.apache.commons.codec.binary.Hex(1.6 版)中的 Hex.encodeHexString(Byte Array) 方法

在我的 Android 平台 (SDK 2.3.1) 上,commons-codec 库版本 1.3 已经存在,但此版本中尚不存在该方法(仅 encodeHex() )。

我将 1.6 版的 jar 库添加到我的 Eclipse 项目中(进入 /libs 目录),但是当我在 Emulator 上运行该项目时,我得到了这个:

E/AndroidRuntime(1632): FATAL EXCEPTION: main
E/AndroidRuntime(1632): java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString

我怎样才能指出好的库在哪里?

我在 Mac OS X 上使用带有 Java 1.6.0 的 Eclipse Juno

对不起,我的英语不好,提前谢谢!

编辑:我的问题显然可以用 jarjar 工具解决。 http://code.google.com/p/google-http-java-client/issues/detail?id=75

有人可以帮助我使用这个工具吗?我不知道如何创建 Ant Manifest 或 jar 文件。

谢谢

【问题讨论】:

  • 正如你所提到的 - 这个问题的解决方法是使用 jarjar 创建一个不会与 android 的类冲突的新 jar。解释 + 解决方案 + 创建的 jar(为我解决了问题) - priyanka-tyagi.blogspot.co.il/2013/03/…(感谢 bianca 为我们节省了时间......)

标签: android apache conflict nosuchmethoderror apache-commons-codec


【解决方案1】:

这是由于 Android 随附的旧 (1.2) 版本的 Commons Codec 与您的较新版本发生冲突而导致的命名空间冲突。虽然阴影是一个很好的解决方法,但我认为从长远来看它不是一个可持续的解决方案。这是一个系统性问题,任何打包在 Android 中的开源库都会出现这种问题。我已向 Google 提交了一个问题。如果你同意,我鼓励你给它“加注星标”,这样它就会得到它需要的关注。这是链接 - https://code.google.com/p/android/issues/detail?id=160578

【讨论】:

  • 不幸的是,Commons-codec 并不是 Android 中唯一使用的遗留库。还有更多(XML、JSON、Apache HTTP 客户端、Javax、bouncycastle 等)。就我而言,阴影是最好的解决方案,因为我已经模块化了我的软件,以便在我的服务器和 android 之间拥有通用代码。我可以在两个平台上以相同的行为运行相同的代码。
【解决方案2】:

nbe_42 答案的扩展版本,包含完整文档...

commons-codec-shaded jar 项目:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>commons-codec</groupId>
    <artifactId>commons-codec-shaded</artifactId>
    <name>Apache Commons Codec (shaded)</name>
    <!-- The version of this project specifies the Apache Commons Codec version which will
         be used, it must therefore match an existing (and preferably current) version. -->
    <version>1.9</version>
    <packaging>jar</packaging>

    <!--
      *************************************************************
       Rationale for this "shaded" version of Apache Commons Codec
      *************************************************************

      Context:
        Android includes an outdated version (v1.3) of commons-codec as an internal library.
        This library is not exposed in the Android SDK so app developers who want to rely on
        commons-codec need to treat it as an addition dependency and include it in the APK
        of their app. However, at runtime Android will always favour its internal version of
        the library which causes trouble when app code tries to call methods that don't
        exist in v1.3 but do exist in the version the developer expected to be using.

      Solution:
        After experimenting with many different variations the current (and final) solution
        to this problem is implemented in this project and does not require big hacks or
        changes in projects which depend on commons-codec, expect for declaring dependency
        on commons-codec-shaded (i.e. this project) instead of the original commons-codec.
        What we do here is take the "original" commons-codec library (currently version 1.9)
        and use the maven-shade-plugin to "shade" it, which means we modify the package name
        of the library (both in the compiled classes and the sources jar) in order to avoid
        the clash with Android's version. The package name is changes from
        "org.apache.commons.codec" to "shaded.org.apache.commons.codec". The result is
        published to the local Maven repository for other projects to use by simple
        dependency declaration on this project. Because we only apply the shading to
        commons-codec itself (and not to other classes using it; which is possible using the
        shade plug-in but doesn't work in combination with android-maven-plugin) any client
        classes which make use of commons-codec will have to import the new "shaded" package
        name instead of the old one.

      Issue on android-maven-plugin github which I posted to discuss all this:
        https://github.com/jayway/maven-android-plugin/issues/487
    -->

    <description>
     The Apache Commons Codec package contains simple encoder and decoders for
     various formats such as Base64 and Hexadecimal.  In addition to these
     widely used encoders and decoders, the codec package also maintains a
     collection of phonetic encoding utilities.
    </description>
    <url>http://commons.apache.org/proper/commons-codec/</url>
    <organization>
        <name>The Apache Software Foundation</name>
        <url>http://www.apache.org/</url>
    </organization>
    <licenses>
        <license>
            <name>The Apache Software License, Version 2.0</name>
            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
            <distribution>repo</distribution>
        </license>
    </licenses>

    <contributors>
        <contributor>
            <name>Matthias Stevens</name>
            <email>m.stevens {at} ucl.ac.uk</email>
            <roles>
                <role>Shading for use on Android</role>
            </roles>
        </contributor>
        <!-- see commons-codec:commons-codec pom for original contributors/developers -->
    </contributors>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>1.6</maven.compiler.source>
        <maven.compiler.target>1.6</maven.compiler.target>
        <commons-codec-package>org.apache.commons.codec</commons-codec-package>
        <shading.prefix>shaded</shading.prefix>
        <shaded-commons-codec-package>${shading.prefix}.${commons-codec-package}</shaded-commons-codec-package>
        <commons-codec-src-folder>${project.build.directory}/commons-codec-src</commons-codec-src-folder>
        <commons-codec-res-folder>${project.build.directory}/commons-codec-res</commons-codec-res-folder>
        <manifest.path>${project.build.directory}/MANIFEST.MF</manifest.path>
        <!-- plugin versions -->
        <dependency-plugin-version>2.9</dependency-plugin-version>
        <compiler-plugin-version>3.2</compiler-plugin-version>
        <antrun-plugin-version>1.7</antrun-plugin-version>
        <jar-plugin-version>2.5</jar-plugin-version>
        <source-plugin-version>2.4</source-plugin-version>
        <shade-plugin-version>2.3</shade-plugin-version>
        <bundle-plugin-version>2.5.3</bundle-plugin-version>
        <!-- taken/modified from: http://svn.apache.org/repos/asf/commons/proper/commons-parent/trunk/pom.xml -->
        <commons.osgi.symbolicName>${shaded-commons-codec-package}</commons.osgi.symbolicName>
        <commons.osgi.export>${shaded-commons-codec-package}.*;version=${project.version};-noimport:=true</commons.osgi.export>
        <commons.osgi.import>*</commons.osgi.import>
        <commons.osgi.dynamicImport />
        <commons.osgi.private />
    </properties>

    <build>
        <finalName>${project.artifactId}</finalName>
        <sourceDirectory>${commons-codec-src-folder}</sourceDirectory>      
        <resources>
            <resource>
                <!-- txt files in shaded\org\apache\commons\codec\language\bm -->
                <directory>${commons-codec-res-folder}</directory>
                <includes>
                    <include>${shading.prefix}/**/*.txt</include>
                </includes>
            </resource>
            <resource>
                <!-- LICENSE & NOTICE files -->
                <directory>${commons-codec-res-folder}/META-INF</directory>
                <targetPath>META-INF</targetPath>
                <includes>
                    <include>*.txt</include>
                </includes>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <!-- fetch & unpack commons-codec sources and resources -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>${dependency-plugin-version}</version>
                <executions>
                    <execution>
                        <id>unpack_commons-codec_sources_and_resources</id>
                        <phase>process-sources</phase>
                        <goals>
                            <goal>unpack</goal>
                        </goals>
                        <configuration>
                            <artifactItems>
                                <!-- commons-codec sources -->
                                <artifactItem>
                                    <groupId>commons-codec</groupId>
                                    <artifactId>commons-codec</artifactId>
                                    <!-- the project version specifies the commons-codec version to use: -->
                                    <version>${project.version}</version>
                                    <classifier>sources</classifier>
                                    <overWrite>true</overWrite>
                                    <excludes>**/*.txt,META-INF/*</excludes>
                                    <outputDirectory>${commons-codec-src-folder}</outputDirectory>
                                </artifactItem>
                                <!-- commons-codec resources (in package) -->
                                <artifactItem>
                                    <groupId>commons-codec</groupId>
                                    <artifactId>commons-codec</artifactId>
                                    <!-- the project version specifies the commons-codec version to use: -->
                                    <version>${project.version}</version>
                                    <classifier>sources</classifier>
                                    <overWrite>true</overWrite>
                                    <includes>org/**/*.txt</includes>
                                    <!-- apply shading: -->
                                    <outputDirectory>${commons-codec-res-folder}/${shading.prefix}</outputDirectory>
                                </artifactItem> -->
                                <!-- commons-codec resources (in META-INF) -->
                                <artifactItem>
                                    <groupId>commons-codec</groupId>
                                    <artifactId>commons-codec</artifactId>
                                    <!-- the project version specifies the commons-codec version to use: -->
                                    <version>${project.version}</version>
                                    <classifier>sources</classifier>
                                    <overWrite>true</overWrite>
                                    <includes>META-INF/*.txt</includes>
                                    <outputDirectory>${commons-codec-res-folder}</outputDirectory>
                                </artifactItem> -->
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <!-- compile commons-codec sources -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${compiler-plugin-version}</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>${jar-plugin-version}</version>
                <executions>
                    <execution>
                        <!-- jar unshaded classes (& resources) -->
                        <id>jar-unshaded</id>
                        <phase>package</phase>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                    <execution>
                        <!-- rejar shaded classes (& resources), with proper manifest partially generated by bundle plugin -->
                        <id>jar-shaded</id>
                        <!-- runs after bundle plugin has done its work to generate bundle manifest -->
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                        <configuration>
                            <archive>
                                <manifestFile>${manifest.path}</manifestFile>
                                <manifestEntries>
                                    <Specification-Title>${project.name}</Specification-Title>
                                    <Specification-Version>${project.version}</Specification-Version>
                                    <Specification-Vendor>${project.organization.name}</Specification-Vendor>
                                    <Implementation-Title>${project.name}</Implementation-Title>
                                    <Implementation-Version>${project.version}</Implementation-Version>
                                    <Implementation-Vendor>${project.organization.name}</Implementation-Vendor>
                                    <Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id>
                                    <Implementation-Build>${implementation.build}</Implementation-Build>
                                    <X-Compile-Source-JDK>${maven.compiler.source}</X-Compile-Source-JDK>
                                    <X-Compile-Target-JDK>${maven.compiler.target}</X-Compile-Target-JDK>
                                </manifestEntries>
                            </archive>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <!-- attach sources jar -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <version>${source-plugin-version}</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                            <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
                        </manifest>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <!-- jar unshaded sources -->
                        <id>attach-unshaded-sources</id>
                        <!-- <phase>package</phase> (default) -->
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                    <execution>
                        <!-- rejar shaded sources -->
                        <id>attach-shaded-sources</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <!-- apply the shading to main jar and sources jar -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>${shade-plugin-version}</version>
                <executions>
                    <execution>
                        <id>shading-main-jar-and-sources-jar</id>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <!-- (not needed as it is the one and only artifact/dependency)
                            <artifactSet> 
                                <includes>
                                    <include>commons-codec:*</include>
                                </includes>
                            </artifactSet>
                            -->
                            <relocations>
                                <relocation>
                                    <pattern>${commons-codec-package}</pattern>
                                    <shadedPattern>${shaded-commons-codec-package}</shadedPattern>
                                </relocation>
                            </relocations>
                            <createDependencyReducedPom>false</createDependencyReducedPom>
                            <!-- (only needed when dependency reduced pom is generated)
                            <dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation> 
                            <keepDependenciesWithProvidedScope>true</keepDependenciesWithProvidedScope> 
                            <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
                            -->
                            <createSourcesJar>true</createSourcesJar>
                            <shadeSourcesContent>true</shadeSourcesContent>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>${antrun-plugin-version}</version>
                <executions>
                    <execution>
                         <!-- unpack shaded classes & sources for manifest generation and re-jarring -->
                        <id>post-shading-tasks</id>
                        <phase>package</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <target>
                                <!-- Unjar shaded classes for generation of manifest -->
                                <echo>Deleting unshaded classes...</echo>
                                <delete dir="${project.build.directory}/classes"/>
                                <echo>Unjarring shaded main jar...</echo>
                                <unzip src="${project.build.directory}/${project.artifactId}.jar" dest="${project.build.directory}/classes"/>
                                <!-- delete to prevent dual inclusion in new main jar -->
                                <delete dir="${project.build.directory}/classes/META-INF/maven"/>
                                <!-- Unjar shaded sources -->
                                <echo>Deleting unshaded sources...</echo>
                                <delete dir="${commons-codec-src-folder}"/>
                                <echo>Unjarring shaded sources jar...</echo>
                                <unzip src="${project.build.directory}/${project.artifactId}-sources.jar" dest="${commons-codec-src-folder}"/>
                                <!-- delete to prevent dual inclusion in new sources jar -->
                                <delete dir="${commons-codec-src-folder}/META-INF"/>
                            </target>
                        </configuration>
                    </execution>
                    <execution>
                            <id>delete-orginals</id>
                            <phase>verify</phase>
                            <goals>
                                <goal>run</goal>
                            </goals>
                            <configuration>
                                <target>
                                    <echo>Deleting unshaded jar files...</echo>
                                    <delete>
                                        <fileset dir="${project.build.directory}" includes="**/original-*.jar" />
                                    </delete>
                                </target>
                            </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <!-- taken/modified from: http://svn.apache.org/repos/asf/commons/proper/commons-parent/trunk/pom.xml -->
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <version>${bundle-plugin-version}</version>
                <configuration>
                    <archive>
                        <forced>true</forced>
                    </archive>
                    <excludeDependencies>true</excludeDependencies>
                    <manifestLocation>${project.build.directory}</manifestLocation>
                    <instructions>
                        <!-- stops the "uses" clauses being added to "Export-Package" manifest entry -->
                        <_nouses>true</_nouses>
                        <!-- Stop the JAVA_1_n_HOME variables from being treated as headers by Bnd -->
                        <_removeheaders>JAVA_1_3_HOME,JAVA_1_4_HOME,JAVA_1_5_HOME,JAVA_1_6_HOME,JAVA_1_7_HOME,JAVA_1_8_HOME</_removeheaders>
                        <Bundle-SymbolicName>${commons.osgi.symbolicName}</Bundle-SymbolicName>
                        <Export-Package>${commons.osgi.export}</Export-Package>
                        <Private-Package>${commons.osgi.private}</Private-Package>
                        <Import-Package>${commons.osgi.import}</Import-Package>
                        <DynamicImport-Package>${commons.osgi.dynamicImport}</DynamicImport-Package>
                        <Bundle-DocURL>${project.url}</Bundle-DocURL>
                    </instructions>
                </configuration>
                <executions>
                    <execution>
                        <id>bundle-manifest</id>
                        <!-- runs after the unjarring of the shaded classes -->
                        <phase>integration-test</phase><!--  default is: process-classes -->
                        <goals>
                            <goal>manifest</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

在 (apk/apklib/aar/jar) 项目中您要使用着色库:

<!-- ... -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec-shaded</artifactId>
    <version>1.9</version>
</dependency>
<!-- ... -->

【讨论】:

  • 非常棒的开箱即用解决方案。我以前是手动“着色”的,效果很好,但这要灵活得多。会议继续在这里:github.com/simpligility/android-maven-plugin/issues/487Google 应该处理这个问题(比如编译器补丁?),或者至少提供这样的解决方案。
【解决方案3】:

关于nbe_42的maven shade插件,插件块放在pom.xml文件的哪个位置很重要,不然maven会漏掉。

对我有用的是把它放在 pom.xml 中 &lt;build&gt; &lt;plugins&gt; 块的末尾:

<build>
<plugins>
{all other plugins}
...
{shade plugin}
</plugins>
</build>

最初我在块的开头有它并且maven没有运行它。

要创建 .jar 文件,请从 apache.org 下载 commons-codec-1.8-src.zip 源 .zip 存档。打开包装。 pom.xml 文件将位于存档的基本目录中。如上所述在 pom.xml 文件中插入 nbe_42 的插件块并运行:

mvn install

这将为您构建、测试、替换和安装插件。

成功的输出应该是这样的:

[INFO] --- maven-shade-plugin:2.2:shade (default) @ commons-codec ---
[INFO] Replacing original artifact with shaded artifact.
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
...`

【讨论】:

    【解决方案4】:

    回复较晚,但可能对某人有用。

    使用Maven Shade Plugin解决的问题

    此插件允许在编译时重命名冲突库的包名。

    用法:

       <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <executions>
                <execution>
                <phase>package</phase>
                <goals>
                    <goal>shade</goal>
                    </goals>
                <configuration>
                        <relocations>
                            <relocation>
                                    <pattern>org.apache.commons</pattern>
                                        <shadedPattern>com.example.shaded.org.apache.commons</shadedPattern>
                            </relocation>
                        </relocations>
                            <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
                </configuration>
                </execution>
            </executions>
        </plugin>
    

    【讨论】:

    • 这看起来是一个很好的答案。然而,当我将它添加到 Maven 构建脚本中时,生成的 APK 不会安装。它说 [INSTALL_PARSE_FAILED_NO_CERTIFICATES] 并且我使用 jarsigner 来验证构建它说“jarsigner: java.lang.SecurityException: Invalid signature file digest for Manifest main attributes” 有什么想法吗?
    • 您的 Maven 构建配置未签署 APK。我使用 maven-jarsigner-plugin 来执行此操作: maven-jarsigner-plugin1.2true 您还应该能够通过正确配置 maven-android-plugin 使用调试密钥对其进行签名。
    • 我已经配置了 jarsigner,这个构建脚本已经工作了几个月了。我在构建执行中包含了我的 maven-jarsigner-plugin 下面的 Shade 插件,现在输出的 SHADED apk 是未签名的。
    • 好的,你的 jarsigner 插件配置正确了吗?通过添加正确的包含? ${project.build.directory}/${project.artifactId}.apk${project.build.directory}/${project.artifactId}-SHADED.apk
    • 感谢您的指点 - 我已经尝试过了,我只是尝试使用终端中的 jarsigner 工具手动签署文件,安装时仍然遇到同样的问题。查看 logcat 似乎失败了,因为它找不到我的 assets/mydatabase.sqlite 文件的任何证书。不确定这是否相关或只是它检查的第一件事。当我弄清楚时我会回来的。感谢您的帮助。
    【解决方案5】:

    您是否已将库添加到项目的构建路径中?完成后,您应该能够从 jar 文件中调用该方法。

    如果它在不添加库的情况下构建良好,那么您可能是针对较新版本的 android 构建然后有效地部署到较旧版本,我发现 String.isEmpty() 不在 2.1 中但是当我在写作时它构建的代码正常,因为我正在构建针对 4.1 的代码

    您可能需要将 jar 与您的项目一起导出(同样,您可以在配置构建路径时执行此操作)。

    希望对你有帮助

    【讨论】:

    • 库被添加到构建路径中。构建工作正常,但我的应用程序在运行时崩溃。我已经尝试使用 JB 或 ICS 的 SDK 但同样的问题.. 谢谢
    猜你喜欢
    • 2019-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-05
    • 1970-01-01
    • 2023-03-25
    • 2017-10-16
    • 2012-01-09
    相关资源
    最近更新 更多