精简JRE & 实现无java环境的PC中运行java程序

做这个的目的:

一天,我玩了一个游戏,名叫《怪兽必须死》,微信手游。游戏的大致内容就是不停的点击游戏界面。。。

说起来是个很无聊的游戏,然后我又复制改编网上的自动点击代码(毕竟这个游戏可以通过电脑微信端登陆玩耍),成功解放我的双手,然后想要分享给我的朋友。这时发现直接成EXE文件对我来说有点难实现。然后就考虑直接打包附带JRE运行。也就是网上一般的使用方式。

步入正题:

先抄一些网上的话。现在运行的java版本最高是1.8版本。虽然我用的是1.7的JRE库。这并不影响这篇文章的主题。

那么我们用的JRE库现在有140+M,但是我(我们)编写的程序却只有几K或者十几K。如果全部COPY,这样得不偿失,于是想到了精简JRE

1、简化jre目录中的BIN下的那些DLL文件以及EXE文件,比如:java.exe之类的(当然,这个是不能删除的。至于虚拟机之类的我不懂就不说了。)

1.1、打包你的程序,放在一个测试的文件夹下。

1.2、复制一份jre目录过去,使用这个目录下的java.exe运行你打包的程序。(可能我说的不是很清楚,请看第三步)

1.3、打开程序后,尝试删除BIN目录下的所有文件。因为程序正在运行,占用需要的EXE或者DLL所以,只会删除不需要的文件。(但是千万不要直接去删除你安装java的目录下的那个jre中的bin中的文件。会爆炸的~~)

1.4、以上,已经把jre中的BIN精简了。精简后的大小应该和程序的内容有关,嗯,我完成后的大小是10.2M。

2、现在精简jre下的lib目录。请允许我分享一个我学习的文章:整理JRE瘦身或精简JRE。http://blog.csdn.net/xiaoping8411/article/details/6973887

3、接下来我来简述一下步骤和内容:

原因:一般(网上说的,要看详细解释的请点击上面的链接)我们用到的也就是rt.jar。那么,弄他~

3.1、使用java中的-verbose命令导出我们程序需要的所有类。

3.1.1、使用方式:使用CMD窗口:java -jar -verbose test.jar >> class.txt     图中那个参数有误。。。。。


3.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文件是我们程序需要用到的。

因为这个输出的内容很有规律,所以,我们可以编一个小程序来完成这些操作。在我借鉴的那篇文章中则是使用了别的方法。

代码的方法:

1、只读取以“[L”为开头的行。

2、使用String类中的split方法,看得出来可以用空格来分隔我们的信息。然后得到的数组中的第二个就是我们类文件的位置

3、遍历每一条信息,把点改成斜杠。

4、贴上代码

/**
 * 
 */
package com.xcx.jing_jian_jre;

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:/E/Praogram Practice/ALL java practice/programOutput/class.txt";
	private static String outAddress = "D:/E/Praogram Practice/ALL java practice/programOutput/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();
		}
	}
}

然后就得到了我们需要的文件信息。这里借用我参考的那篇文章中的代码,再次推荐那篇文章,嗯,他的代码:整理JRE瘦身或精简JRE~http://blog.csdn.net/xiaoping8411/article/details/6973887

我就直接复制了,如果有问题,请联系我删除。。。

/**
 * 
 */
package com.xcx.jing_jian_jre;

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 String source = "D:\\E\\Praogram Practice\\ALL java practice\\programOutput\\lib\\"; // 类源目录
	private String dest = "D:\\E\\Praogram Practice\\ALL java practice\\programOutput\\lib\\"; // 类拷贝目的目录
	String[] jarArr = new String[] { "rt", "charsets" };

	/***
	 * 
	 * @param source
	 *            类源目录
	 * @param dest
	 *            类拷贝目的目录
	 * @param jarArr
	 *            需要的提取的jar文件
	 */
	public CopyClass(String source, String dest, String[] jarArr) {
		this.source = source;
		this.dest = dest;
		this.jarArr = jarArr;
	}

	public static void main(String[] args) {
		String[] jarArr = new String[] { "rt", "charsets" };
		CopyClass obj = new CopyClass("D:\\E\\Praogram Practice\\ALL java practice\\programOutput\\lib\\",
				"D:\\E\\Praogram Practice\\ALL java practice\\programOutput\\lib\\", jarArr);
		obj.readAndCopy("D:\\E\\Praogram Practice\\ALL java practice\\programOutput\\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、在使用copyclass这个类的时候,使用的是经过加工的class_demo2.txt。

3、一定要在那个RT.JAR文件夹中,把这个RT.JAR解压,不然是找不到路径的,其他的jar包也是一样。

4、输出的文件夹名称比解压的包名字多了一个1。


OK

感谢你看完我BB这么多~~~

觉得好请点赞打赏并粉一下我~

如果我哪里出错了请毫不犹豫的指出来,十分感谢。

一起加油~



阅读更多
换一批

没有更多推荐了,返回首页