精简JRE其实就是根据需要运行的jar,提取jre目录中必要的文件
第一步:简化bin文件夹
1、简化jre目录中的BIN下的那些DLL文件以及EXE文件,当然java.exe这个是不能删除的。
1.1、打包你的程序,放在一个测试的文件夹下。
1.2、复制一份jre目录过去,使用这个目录下的java.exe运行你打包的程序。
.\jre\bin\java.exe -jar test.jar
1.3、打开程序后,尝试删除BIN目录下的所有文件。因为程序正在运行,占用需要的EXE或者DLL所以,只会删除不需要的文件。(注意是删除测试目录下的那个jre中的bin下面的文件)
1.4、以上,已经把jre中的BIN精简了。精简后的大小应该和程序的内容有关。
第二步:简化lib文件夹
2、简化lib目录中的jar, 仅保留程序需要的所有类
2.1、使用java中的-verbose命令导出我们程序需要的所有类,使用CMD窗口:
java -jar -verbose test.jar >> class.txt
这个时候会打开你的程序,然后将该程序的所有功能执行一遍,这才会将所有需要用到的类输出到class.txt文件。
2.2、程序需要的类库信息就在那个class.txt文件中。文件的大致内容就是:
[Opened D:\E\cs\jre\lib\rt.jar] [Loaded java.lang.Object from D:\E\cs\jre\lib\rt.jar] [Loaded java.io.Serializable from D:\E\cs\jre\lib\rt.jar] [Loaded java.lang.Comparable from D:\E\cs\jre\lib\rt.jar] ...... [Opened D:\E\cs\jre\lib\charsets.jar]
所以,我的程序需要用到rt.kar和charsets.jar两个包,就是通过那个opened看你需要哪些包。然后通过loaded后面的信息来找到哪些class文件是我们程序需要用到的。
2.3、在class.txt文件提取需要用到的类的信息列表:
因为这个输出的内容很有规律,所以,我们可以编一个小程序来完成这些操作
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; /** * @author soil */ public class Demo_Class2 { private static String sourse = "D:/demo/class.txt"; private static String outAddress = "D:/demo/class_demo2.txt"; public static void main(String[] args) { try { int count_L = 0; int loopCount = 0; // 源文件位置,打开它 FileInputStream fin = new FileInputStream(sourse); InputStreamReader isr = new InputStreamReader(fin); BufferedReader br = new BufferedReader(isr); // 输出文件位置 FileOutputStream fout = new FileOutputStream(outAddress); OutputStreamWriter osw = new OutputStreamWriter(fout); BufferedWriter bw = new BufferedWriter(osw); // 读一行 String sp = br.readLine(); // 只要没有读到文件尾就一直执行 while (sp != null) { // 只读取以"[L"为开头的行 if (sp.substring(0, 2).equals("[L")) { // 以空格来分隔这个行,返回的字符串数组中的第二个就是我们需要的信息 String s = sp.split(" ")[1]; StringBuilder bs = new StringBuilder(s); // 只是个测试输出,可以不加 System.out.println(bs); // 循环遍历这个字符串,修改它,使它变成我们需要的格式 for (int i = 0; i < bs.length(); i++) { char ch = bs.charAt(i); // 简化循环,因为我们得到的信息很有规律。只要出现大写的字母,就说明已经到了不需要执行的时候了。 if (ch >= 65 && ch < 91) break; // 把'.'替换成'/',当然,代码中是因为方法的参数要求。 if (ch == '.') { bs.replace(i, i + 1, "/"); } // 这个是循环的执行此时。 loopCount++; } // 这里在输出你的文件信息。加工后用于后续操作。 bw.write(bs.toString() + '\n'); // 程序需要的类文件数目。 count_L++; } // 读行 sp = br.readLine(); } // 两个测试输出 System.out.println(count_L); System.out.println(loopCount); // 千万要把两个文件关闭!!! // 重要的事情说三遍,如果没有关闭,数据可能不能完全输出。 // 个人认为这个和数据的大小有一定关系。还和输出数据的格式有关系。虽然我说不清楚,但是,一定要关闭,不然就要炸,boom~~ br.close(); bw.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
2.4、解压对应需要操作的jar文件,根据需要用到的类的信息列表,拷贝需要的类,同样通过程序去处理,使用的是经过加工的class_demo2.txt:
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; /** * @author soil */ public class CopyClass { private static String source = "D:\\demo\\jre\\lib\\"; // 类源目录 private static String dest = "D:\\demo\\jre\\lib\\"; // 类拷贝目的目录 private static String[] jarArr = new String[] { "rt", "charsets" };//解压jar文件后的文件夹名 public static void main(String[] args) { CopyClass obj = new CopyClass(); obj.readAndCopy("D:\\demo\\class_demo.txt"); } /*** * @param logName * 提取class明细 */ public void readAndCopy(String logName) { int count = 0; // 用于记录成功拷贝的类数 try { FileInputStream fi = new FileInputStream(logName); InputStreamReader ir = new InputStreamReader(fi); BufferedReader br = new BufferedReader(ir); String string = br.readLine(); while (string != null) { if (copyClass(string) == true) count++; else System.out.println("ERROR " + count + ": " + string); string = br.readLine(); } br.close(); } catch (IOException e) { System.out.println("ERROR: " + e); } System.out.println("count: " + count); } /** * 从原jar路径提取相应的类到目标路径,如将java/lang/CharSequence类从rt目录提取到rt1目录 * * @param string * 提取类的全路径 * @return * @throws IOException */ public boolean copyClass(String string) throws IOException { String classDir = string.substring(0, string.lastIndexOf("/")); String className = string.substring(string.lastIndexOf("/") + 1, string.length()) + ".class"; FileOutputStream fout; FileInputStream fin; boolean result = false; for (String jar : jarArr) { File srcFile = new File(source + "/" + jar + "/" + classDir + "/" + className); if (!srcFile.exists()) { continue; } byte buf[] = new byte[256]; fin = new FileInputStream(srcFile); /* 目标目录不存在,创建 */ File destDir = new File(dest + "/" + jar + "1/" + classDir); if (!destDir.exists()) destDir.mkdirs(); File destFile = new File(destDir + "/" + className); fout = new FileOutputStream(destFile); int len = 0; while ((len = fin.read(buf)) != -1) { fout.write(buf, 0, len); } fout.flush(); result = true; break; } return result; } }
输出的文件夹名称比解压的包名字多了一个1
2.5、将提取程序需要的类后重新打包成jar文件,进入需要打包的文件夹内,使用CMD窗口:
用法:jar cvf [生成文件名.jar] [-C 目录] 文件名
jar cvf name.jar .
2.6、替换lib下对应的jar,完成
本文参考文章:
https://blog.csdn.net/ema1995cylove/article/details/52792361
注意:本文归作者所有,未经作者允许,不得转载