使用ProGuard合并多个jar文件

本文介绍如何使用ProGuard将多个Jar文件缩减为一个Jar文件,以剔除未使用的类,减少应用程序体积。通过Ant脚本实现自动化处理,保留必要的类和方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值