用于 Android Proguard 混淆的 ANT 构建

Posted

技术标签:

【中文标题】用于 Android Proguard 混淆的 ANT 构建【英文标题】:ANT build for Android Proguard obfuscation 【发布时间】:2011-06-05 11:49:32 【问题描述】:

任何人都可以分享适用于 android 的示例/简单混淆 ANT 任务吗?前提是我确实有完整的 APK,我只需要通过 *class hru Proguard 然后准备 *.dex 来构建 APK

【问题讨论】:

【参考方案1】:

我找到了解决办法:

    获取Proguard - 将 proguard.jar 复制到已知目录(例如 MyProject/proguard) 准备 proguard.cfg - 描述优化/混淆的内容和方式。这个过程在 Proguard 的manual 中有详细描述 准备遵循 ANT 的 build.xml(或类似这个) - 非常感谢 this guy

更新完成 build.xml

<?xml version="1.0" encoding="UTF-8"?> 
<project name="MyProject" default="zipalign" basedir=".">
    <property name="target" value="android-8"/>
    <!--property file="default.properties" /-->
    <property name="encoding" value="UTF-8"/>

    <!-- dirs -->
    <property name="sdk.dir" location="Location of Android SDK"/>
    <property name="proguard.dir" value="proguard" />
    <property name="src.dir" value="src"/>
    <property name="gen.dir" value="gen"/>
    <property name="res.dir" value="res"/>
    <property name="assets.dir" value="assets"/>
    <property name="libs.dir" value="libs"/>
    <property name="out.classes.unoptimized.dir" value="out"/>
    <property name="out.classes.optimized.dir" value="out/optimized"/>

    <!-- files -->
    <property name="manifest.file" value="AndroidManifest.xml"/>
    <property name="signed.apk" value="$ant.project.name-signed.apk"/>
    <property name="unsigned.apk" value="$ant.project.name-unsigned.apk"/>
    <property name="final.apk" value="$ant.project.name.apk"/>
    <property name="android.jar" value="$sdk.dir/tools/platforms/$target/android.jar"/>
    <property name="unoptimized" value="unoptimized.jar" />
    <property name="optimized" value="optimized.jar" />
    <property name="proguard.config" value="$proguard.dir/proguard.cfg"/>

    <!-- tools -->
    <property name="dx.jar" value="$sdk.dir/platform-tools/lib/dx.jar"/>
    <property name="aapt" value="$sdk.dir/platforms/$target/tools/aapt.exe"/>
    <property name="zipalign" value="$sdk.dir/tools/zipalign.exe"/>
    <property name="jarsign" value="jarsigner.exe location is here"/>
    <property name="keystore" value="Your key store is here"/>
    <property name="keyalias" value="Your key alias is here"/>


    <path id="android.antlibs">
        <pathelement path="$sdk.dir/tools/lib/anttasks.jar" />
        <pathelement path="$sdk.dir/tools/lib/sdklib.jar" />
        <pathelement path="$sdk.dir/tools/lib/androidprefs.jar" />
        <pathelement path="$sdk.dir/tools/lib/apkbuilder.jar" />
        <pathelement path="$sdk.dir/tools/lib/jarutils.jar" />
    </path>

    <taskdef name="setup"
        classname="com.android.ant.SetupTask"
        classpathref="android.antlibs" />
    <setup import="false"/>

    <!--taskdef name="aaptexec"
            classname="com.android.ant.AaptExecLoopTask"
            classpathref="android.antlibs" /-->

    <target name="clean" description="Removes output files created by other targets.">
        <echo>Cleaning...</echo>
        <delete dir="$out.classes.unoptimized.dir" verbose="true" />
        <delete dir="$out.classes.optimized.dir" verbose="true" />
    </target>

    <target name="dirs">
        <echo>Creating output directories if needed...</echo>
        <mkdir dir="$out.classes.unoptimized.dir" />
        <mkdir dir="$out.classes.optimized.dir" />
    </target>

    <!-- Compiles this project's .java files into .class files. -->
    <target name="compile" depends="dirs"
            description="Compiles project's .java files into .class files">
        <echo>Compiling sources...</echo>
        <javac encoding="$encoding" target="1.6" debug="true" extdirs=""
                destdir="$out.classes.unoptimized.dir"
                bootclasspathref="android.target.classpath"
                includeantruntime="true">
            <src path="$src.dir" />
            <src path="$gen.dir" />
            <classpath>
                <fileset dir="$libs.dir" includes="*.jar" />
            </classpath>
        </javac>
    </target>

    <target name="preobfuscate" depends="compile">
        <echo>Preparing to obfuscation...</echo>
    <jar destfile="$unoptimized"
       basedir="$out.classes.unoptimized.dir"
       includes="**/**"
       excludes="optimized/**"
       />
    </target>

    <!--  Obfuscation with ProGuard   -->
    <target name="optimize" unless="nooptimize" depends="preobfuscate">
        <echo>Proguard obfuscation...</echo>
        <java jar="$proguard.dir/proguard.jar" fork="true" failonerror="true">
            <jvmarg value="-Dmaximum.inlined.code.length=16" />
            <arg value="@$proguard.dir/proguard.cfg" />
            <arg value="-injars $unoptimized" />
            <arg value="-outjars $optimized" />
            <arg value="-libraryjars $android.jar" />
        </java>
        <unzip src="$optimized" dest="$out.classes.optimized.dir" />
        <!-- Delete optimized jar (now unzipped into bin directory)  -->
        <delete file="$optimized"/>
        <delete file="$unoptimized"/>
    </target>

    <target name="dex" description="Converting JVM bytecodes into Dalvik bytecodes" depends="optimize">
        <echo>Converting bytecodes to Dalvik VM bytecodes...</echo>
        <java jar="$dx.jar" fork="true">
            <arg line="--dex --verbose --output=$out.classes.optimized.dir/classes.dex $out.classes.optimized.dir"/>
        </java>
    </target>

    <target name="aapt" depends="dex" description="compile resources">
        <echo>Packing resources...</echo>
        <exec executable="$aapt" logerror="true" osfamily="windows">
            <arg line="p
            -f
            -M $manifest.file
            -I $android.jar
            -S $res.dir
            -A $assets.dir
            -F $out.classes.optimized.dir/$unsigned.apk
            -m -J $gen.dir"/>
        </exec>
    </target>

    <target name="sign" depends="aapt" description="sign apk">
        <input message="Please enter keystore password (store:$keystore):"
                       addproperty="keystore.password" />
        <echo>Signing apk...</echo>
        <exec executable="$jarsign" logerror="true" osfamily="windows">
            <arg line="-verbose
            -keystore $keystore
            -storepass $keystore.password
            -signedjar $out.classes.optimized.dir/$signed.apk
            $out.classes.optimized.dir/$unsigned.apk $keyalias"/>
        </exec>
    </target>

    <target name="zipalign" depends="sign" description="zip align">
        <echo>Aligning apk...</echo>
        <exec executable="$zipalign" logerror="true" osfamily="windows">
            <arg line="-f
            -v
            4
            $out.classes.optimized.dir/$signed.apk
            $final.apk"/>
        </exec>
    </target>

</project>

    这个 ANT 任务必须在 Java builder 之后和 Android package builder 之前添加到 Eclipse 的构建器(属性/构建器)任务中。

    按“Build All”(最好在 Eclipse 菜单中关闭 Automatic Build check)

【讨论】:

你会发布完整的 build.xml 吗? 为什么添加“最好在 Eclipse 菜单中关闭自动构建检查”?这是否比默认的 Eclipse Java/Android 构建器慢得多?如果是这样,仅仅是因为 Proguard 步骤,还是 ant 构建过程不善于检测依赖关系? @eros 我已经发布了完整的 build.xml【参考方案2】:

Android构建过程首先将Java源文件(.java)编译成Java类文件(.class),然后将这些类文件转换成Dalvik代码(classes.dex),最后将这个Dalvik代码打包成一个APK文件。

ProGuard 读取和写入 Java 类文件,因此它必须插入到编译步骤和转换步骤之间的这个管道中。它本身不读取或写入 Dalvik 代码,因此无法处理 APK 文件。

Android SDK documentation on ProGuard 讨论了如何在适用于 android-9 的 Ant 构建中启用混淆步骤。简而言之,你必须在文件default.properties中添加一行“proguard.config=proguard.cfg”,然后运行“ant release”。

【讨论】:

这不是我要问的...我正在从 IDE (Intellij) 内部构建 Android APK,在构建之前或构建之后我可以启动任意 ANT 构建任务。是否有可能以某种方式为此定义单独的 ANT 任务? 由于 ProGuard 步骤必须在构建管道的中间执行,因此不可能在 IDEA 构建之前或之后简单地添加 Ant 任务。您必须为整个构建过程创建一个 Ant build.xml 脚本并对其进行修改。 Android SDK 中的 android 命令会为您创建一个 Ant 文件。或者,IDEA 可以为您创建一个 Ant 文件(Build > Generate Ant build...)。 IDEA 的 IDEA Android 插件似乎已经使用 Android SDK 创建了 Ant 文件。然后,您可以从 IDEA 运行 Ant 构建(右侧的菜单栏,假设 Ant 插件已启用)。【参考方案3】:

注意:barmaley 的回复来自 2011 年,似乎适用于 Android SDK Tools 版本 8 或 10。

我尝试使用 Android SDK 工具 18.1.1 版调整此解决方案,但始终因错误而失败:taskdef class com.android.ant.SetupTask cannot be found

最终,我所做的是:

rm build.xml
android update project -p .
如果您的 PATH 中没有 SDK 工具,则需要使用 android 工具的完整路径,例如在 Windows 上:C:\Android\sdk\tools

这创建了一个新的 build.xml,它与当前的 SDK 工具兼容,并且似乎自动化了 barmaley 回复中描述的许多手动工作。

之后我可以运行 ant release,它负责构建和混淆开箱即用的结果 .apk 文件。

为了通过 ant 自动进行混淆,您需要:

    Enable Proguard obfuscator(显然) 创建一个ant.properties 文件并用适当的key.store 参数填充它(有关详细信息,请参阅this SO reply)。

【讨论】:

【参考方案4】:

proGuard 混淆过程需要 .class 文件,因此您无法在 IDE 构建(.java)之前或之后(.dex 打包)启动 Ant。

查看这篇文章,其中解释了如何在全局 Ant 构建中添加 proGuard 步骤:

http://www.androidengineer.com/2010/07/optimizing-obfuscating-and-shrinking.html

如果你真的想使用 IDEA 构建,可以尝试以下方法。

IDEA 构建后使用 apktool 解压 apk。 使用 dex2jar 将 .dex 文件转换为 .class 运行 proGuard,如上一篇文章所示

对不起,我没有附上 apktool 和 dexjar 的链接,但由于我是新手,我不能发布多个超链接。

【讨论】:

我知道,但我只需要用于 Ant 的 build.xml :)

以上是关于用于 Android Proguard 混淆的 ANT 构建的主要内容,如果未能解决你的问题,请参考以下文章

Android ProGuard - 只有混淆

如何使用 proguard 混淆 android 库(.aar)?

Android 库 proguard 包混淆产生 a.a.a.a.a 冲突

Android 程序代码进行代码混淆

ProGuard代码混淆技术详解

导出已签名、混淆的 Android 应用程序时,如何在 Eclipse 中设置 ProGuard?