在构建依赖于库项目的项目时,“java.lang.IllegalArgumentException:已添加”

时间:2012-01-31 23:43:35

标签: android

鉴于

Commons - simple java project
AndroidLibrary1 - android library
AndroidLibrary2 - android library
AndroidProject - android project

项目之间有这样的引用:

AndroidLibrary1 -> Commons
AndroidLibrary2 -> Commons
AndroidProject -> AndroidLibrary1, AndroidLibrary2

问题

当我构建AndroidProject时,我收到了这样的错误:

java.lang.IllegalArgumentException: already added: Lcom/test/Bar;

其中“com.test.Bar”是来自“Commons”项目的类,AndroidLibrary1和AndroidLibrary2都使用该类。

环境

Eclipse 3.7.1
android-sdk-windows_r15

任何想法如何解决这个问题?

修改 找到相关的discussion

1 个答案:

答案 0 :(得分:2)

经过相当长的时间和正则表达式黑魔法后,我找到了这个解决方案:

<target name="-post-compile">
    <macrodef name="dex-helper">
        <element name="external-libs" optional="yes" />
        <attribute name="nolocals" default="false" />
        <sequential>
            <!-- sets the primary input for dex. If a pre-dex task sets it to
                something else this has no effect -->
            <property name="out.dex.input.absolute.dir" value="${out.classes.absolute.dir}" />

            <!-- set the secondary dx input: the project (and library) jar files
                If a pre-dex task sets it to something else this has no effect -->
            <if>
                <condition>
                    <isreference refid="out.dex.jar.input.ref" />
                </condition>
                <else>
                    <path id="out.dex.jar.input.ref">
                        <path refid="jar.libs.ref" />
                    </path>
                </else>
            </if>

            <if>
                <condition>
                    <length string="${toString:out.dex.jar.input.ref}" trim="true" when="greater" length="0"/>
                </condition>
                <then>
                    <echo message="${toString:out.dex.jar.input.ref}" file="antler.tmp" />
                    <loadfile property="out.dex.jar.input.ref.fixed" srcFile="antler.tmp">
                        <filterchain>
                            <tokenfilter>
                                <replaceregex
                                    pattern="(?&lt;=;|^)[^;]+\\libs\\([^\\;]+)(?:;|$)(?=(?:(?&lt;=;|^)[^;]+(?:;|$))*(?&lt;=;|^)[^;]+\\\1(?:;|$))"
                                    replace="" flags="g"/>
                            </tokenfilter>
                        </filterchain>
                    </loadfile>
                    <delete file="antler.tmp"/>
                    <path id="out.dex.jar.input.ref.fixed" path="${out.dex.jar.input.ref.fixed}"/>
                </then>
                <else>
                    <path id="out.dex.jar.input.ref.fixed" />
                </else>
            </if>

            <dex executable="${dx}"
                    output="${intermediate.dex.file}"
                    nolocals="@{nolocals}"
                    verbose="${verbose}">
                <path path="${out.dex.input.absolute.dir}"/>
                <path refid="out.dex.jar.input.ref.fixed" />
                <external-libs />
            </dex>
        </sequential>
    </macrodef>
</target>

这是您应该在<import file="${sdk.dir}/tools/ant/build.xml" />行之前添加到 build.xml 文件的内容。如果您在多个项目的libs文件夹中具有相同的JAR,则此代码将根据文件名从构建系统中删除重复的条目。

现在,这是如何工作的

我将在libs文件夹中使用“library”一词用于JAR,而在其他项目可能依赖的Android库项目中使用“library project”。问题是,当Android系统使用库项目构建项目时,它只是将所有库捆绑到一个堆中,而忽略了可能存在重复的事实。所以我们必须修复路径。

因此,当构建到达编译完成时,我们重新定义负责将Java代码转换为DEX的部分。上面的代码与Android构建工具完全相同,但以下部分除外:

<if>
    <condition>
        <length string="${toString:out.dex.jar.input.ref}" trim="true" when="greater" length="0"/>
    </condition>
    <then>
        <echo message="${toString:out.dex.jar.input.ref}" file="antler.tmp" />
        <loadfile property="out.dex.jar.input.ref.fixed" srcFile="antler.tmp">
            <filterchain>
                <tokenfilter>
                    <replaceregex
                        pattern="(?&lt;=;|^)[^;]+\\libs\\([^\\;]+)(?:;|$)(?=(?:(?&lt;=;|^)[^;]+(?:;|$))*(?&lt;=;|^)[^;]+\\\1(?:;|$))"
                        replace="" flags="g"/>
                </tokenfilter>
            </filterchain>
        </loadfile>
        <delete file="antler.tmp"/>
        <path id="out.dex.jar.input.ref.fixed" path="${out.dex.jar.input.ref.fixed}"/>
    </then>
    <else>
        <path id="out.dex.jar.input.ref.fixed" />
    </else>
</if>

我们在out.dex.jar.input.ref中有不洁路径,我们的目标是创建没有重复条目的路径(它们将存储在out.dex.jar.input.ref.fixed中)。不幸的是我们必须卸载文件的路径,更改它并加载它,因为ant出于某些奇怪的原因无法在属性中使用正则表达式(除非你使用像ant-contrib这样的外部库)。

执行该技巧的正则表达式是(?<=;|^)[^;]+\\libs\\([^\\;]+)(?:;|$)(?=(?:(?<=;|^)[^;]+(?:;|$))*(?<=;|^)[^;]+\\\1(?:;|$))。这个可怕的符号混乱匹配以libs\<something.jar>结尾的路径,并且还有另一条以<something.jar>结尾的路径。 我的正则表达式使用Windows分隔符,因此如果您使用的是Linux或Mac OS X,则应将这些\\更改为必要的分隔符或使正则表达式具有通用性。对不起,我已经没有心情去解决它已经花了太多时间。如果您想查看正则表达式的工作原理,可以在Regexr上完成。

删除重复项后,我们只需将字符串加载回新路径out.dex.jar.input.ref.fixed并使用它来运行dex。希望我的解决方案有所帮助。