ProGuard(http://proguard.sourceforge.net/)對jar檔案可以進行縮減,最佳化和混淆等操作。縮減jar檔案將多個輸入jar檔案(injar參數)合成一個輸出jar檔案(outjar參數)。最佳化執行與編譯器最佳化類似的操作。基本的混淆操作將變數名、方法名等替換為較短的別名。 這裡利用ProGuard的第一個功能(縮減)將多個jar檔案合成一個jar檔案,並剔除這些檔案中用不到的類。
注意:在縮減和混淆操作中,為保證結果的正確,ProGuard能夠自動處理Class.forName("MyClass") 和 MyClass.class這樣的結構,因而不會將類MyClass剔除或混淆; 在某些情況下,如果ProGuard不能作出決定會警告使用者。
例如有一個應用程式myapp.jar, 該應用程式用到Apache commons-lang3-3.0庫,但是只用到個別的類,為了避免在發布時將Apache commons-lang3-3.0庫和myapp.jar一起發布,可以通過ProGuard的縮減功能將這兩個jar檔案合并成一個jar:myapp-dist.jar(註:不清楚這樣做是否違反了Apache的license約定)。
執行build.xml的run target之前的目錄結構:
./src/org/test/proguard/Main.java
./commons-lang3-3.0/commons-lang3-3.0.jar
./lib/proguard.jar
./build.xml
./manifest.mf
執行完build.xml的run任務之後的目錄結構:
./src/org/test/proguard/Main.java
./commons-lang3-3.0/commons-lang3-3.0.jar
./lib/proguard.jar
./build/org/test/proguard/Main.class
./out/myapp.jar
./lib/myapp-dist.jar
./build.xml
./manifest.mf
其中./build/org/test/proguard/Main.class是compile target產生的;./out/myapp.jar是jar target產生的;./lib/myapp-dist.jar是shrink target產生的,這個檔案就是合并之後的jar檔案。
檔案清單:
myapp.jar/Main.java:
package org.test.proguard;import org.apache.commons.lang3.StringUtils; public class Main {public static void main(String[] args) {String text = "ThisIsAVeryLongLongText";String shorttext = StringUtils.abbreviate(text, 15);System.out.println(text);System.out.println(shorttext);}}
build.xml: (注: 沒有使用ProGuard的最佳化和混淆功能:optimize="off" obfuscate ="off")
<?xml version="1.0" encoding="UTF-8"?><project name="proguard" default="run" basedir="./"><property name="package.dir" value="org/test/proguard"/><property name="javahome" value="/usr/local/jdk1.5.0_16" /><property name="jdk.lib.dir" value="${javahome}/lib" /><property name="jre.lib.dir" value="${javahome}/jre/lib" /><property name="proguard.lib" value="./lib" /><property name="commons.lang.lib" value="./commons-lang3-3.0/" /><property name="src.dir" value="./src" /><property name="build.dir" value="./build" /><property name="out.dir" value="./out" /><property name="dist.dir" value="./lib" /><property name="manifest.file" value="manifest.mf"/><property name="outjar.file" value="myapp.jar"/><property name="distjar.file" value="${dist.dir}/myapp-dist.jar"/><path id="lib.path"><fileset dir="${jdk.lib.dir}"><include name="**/*.jar" /></fileset><fileset dir="${jre.lib.dir}"><include name="**/*.jar" /></fileset><fileset dir="${proguard.lib}"><include name="**/*.jar" /></fileset><fileset dir="${commons.lang.lib}"><include name="**/*.jar" /></fileset></path><target name="clean"><tstamp/><delete includeEmptyDirs="true" quiet="yes"><fileset dir="${build.dir}" /><fileset dir="${out.dir}" /></delete></target><target name="compile" depends="clean"> <mkdir dir="${build.dir}" /><javac srcdir="${src.dir}/${package.dir}" destdir="${build.dir}" debug="yes"><classpath refid="lib.path" /></javac></target><target name="jar" depends="compile"> <mkdir dir="${out.dir}" /> <jar jarfile="${out.dir}/${outjar.file}" manifest="${manifest.file}"> <fileset dir="${build.dir}"> <include name="**/*"/> </fileset> </jar> </target><target name="shrink" depends="jar"> <taskdef resource="proguard/ant/task.properties" classpathref="lib.path"/> <proguard optimize="off" obfuscate ="off" printseeds="on" printmapping="out.map" renamesourcefileattribute="SourceFile"> <!-- Specify the input jars, output jars, and library jars. --> <injar file="${out.dir}/${outjar.file}" /> <injar file="${commons.lang.lib}/commons-lang3-3.0.jar" /> <outjar file="${distjar.file}" /> <libraryjar file="${java.home}/lib/rt.jar" /> <!-- libraryjar file="junit.jar" / --> <!-- libraryjar file="servlet.jar" / --> <!-- libraryjar file="jai_core.jar" / --> <!-- ... / --> <!-- Preserve line numbers in the obfuscated stack traces. --> <keepattribute name="LineNumberTable" /> <keepattribute name="SourceFile" /> <!-- Preserve all annotations. --> <keepattribute name="*Annotation*" /> <!-- Preserve all public applications. --> <keepclasseswithmembers access="public"> <method access ="public static" type ="void" name ="main" parameters="java.lang.String[]" /> </keepclasseswithmembers> <!-- Preserve all native method names and the names of their classes. --> <keepclasseswithmembernames> <method access="native" /> </keepclasseswithmembernames> <!-- Preserve the methods that are required in all enumeration classes. --> <keepclassmembers extends="java.lang.Enum"> <method access="public static" type="**[]" name="values" parameters="" /> <method access="public static" type="**" name="valueOf" parameters="java.lang.String" /> </keepclassmembers> <!-- Explicitly preserve all serialization members. The Serializable interface is only a marker interface, so it wouldn't save them. You can comment this out if your library doesn't use serialization. If your code contains serializable classes that have to be backward compatible, please refer to the manual. --> <keepclassmembers implements="java.io.Serializable"> <field access ="static final" type ="long" name ="serialVersionUID" /> <field access ="static final" type ="java.io.ObjectStreamField[]" name ="serialPersistentFields" /> <method access ="private" type ="void" name ="writeObject" parameters="java.io.ObjectOutputStream" /> <method access ="private" type ="void" name ="readObject" parameters="java.io.ObjectInputStream" /> <method type ="java.lang.Object" name ="writeReplace" parameters="" /> <method type ="java.lang.Object" name ="readResolve" parameters="" /> </keepclassmembers> <!-- Your application may contain more items that need to be preserved; typically classes that are dynamically created using Class.forName --> </proguard></target><target name="run" depends="shrink"> <!--run: java -jar lib/app-dist.jar --> <java jar="${distjar.file}" fork="yes"> </java></target></project>