统计JAR包DEX文件中的方法数目

    前段时间做Android项目中,一直出现方法数超过65535的问题,如果混淆后代码中的方法数目没有超过65535,可以通过

在project.properties文件中加上一行dex.force.jumbo=true,解决这个问题。

  后来自己参考了网上的一些方法,写了个小工具用来统计JAR包和DEX文件中的方法数目。主要原理就是利用DEX的文件结构的文件头中有个method_ids_siz来统计方法数目。

现在分享出来,代码如下,直接拷贝编译成JAR包,然后控制台:java -jar XXX.jar file 就可以看到输出结果了。目前支持文件夹、JAR和DEX文件。

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;

public class Test2 {

	private void test() {
	}

	private final static String JAR = "jar";
	private final static String DEX = "dex";
	private final static String DIR = "dir";
	private final static String UNKNOWN = "unknown";

	/**
	 * 根据扩展名获取文件类型
	 */
	public static String getFileType(String path) {
		String type = UNKNOWN;
		try {
			File file = new File(path);
			if (file.isDirectory()) {
				type = DIR;
			} else {
				if (getExtensionName(file.getName()).equalsIgnoreCase(DEX)) {
					type = DEX;
				} else if (getExtensionName(file.getName()).equalsIgnoreCase(
						JAR)) {
					type = JAR;
				}
			}
		} catch (Exception e) {
		}
		return type;

	}

	public static void main(String[] args) {

		if (args.length != 1) {
			System.out.println("Param Error");
		} else {
			String type = getFileType(args[0]);
			if (type.equalsIgnoreCase(DEX)) {
				resolveDex(args[0]);
			} else if (type.equalsIgnoreCase(JAR)) {
				resolveJar(args[0]);
			} else if (type.equalsIgnoreCase(DIR)) {
				resolveDir(args[0]);
			} else {
				System.err.println("Unknown File Type");
			}
		}
		System.out.println("解析结束");
	}

	/**
	 * 简单的获取扩展名,不完全准确。完全准确的话,可以根据文件流判断。
	 * @param filename
	 * @return
	 */
	public static String getExtensionName(String filename) {
		if ((filename != null) && (filename.length() > 0)) {
			int dot = filename.lastIndexOf(‘.‘);
			if ((dot > -1) && (dot < (filename.length() - 1))) {
				return filename.substring(dot + 1);
			}
		}
		return filename;
	}

	/**
	 * 处理dex文件
	 *
	 * @param path
	 */
	public static void resolveDex(String path) {
		try {
			File file = new File(path);
			FileInputStream fis = new FileInputStream(file);
			byte[] bytes = new byte[1000];
			if (fis.read(bytes) != -1) {
				StringBuilder sb = new StringBuilder();
				for (int i = 91; i > 87; i--) {
					sb.append(Integer.toBinaryString(bytes[i] & 255));
				}
				System.out.println(file.getName() + " 方法数目 : "
						+ Integer.parseInt(sb.toString(), 2));
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 解析JAR
	 *
	 * @param path
	 */
	public static void resolveJar(String filePath) {
		try {
			String path = System.getenv("dx");
			ProcessBuilder pb=new ProcessBuilder("dx.bat","--dex","--output=C://temp.dex",filePath);
			pb.directory(new File(path));
			pb.redirectErrorStream(true);
			Process p =pb.start();
			BufferedInputStream in = new BufferedInputStream(p.getInputStream());
			BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
			String lineStr;

			while ((lineStr = inBr.readLine()) != null) {
				// 获得命令执行后在控制台的输出信息
//					System.out.println(lineStr);// 打印输出信息
			}

			// 检查命令是否执行失败。
			if (p.waitFor() != 0) {
				if (p.exitValue() != 0){// p.exitValue()==0表示正常结束,1:非正常结束
					System.err.println(filePath+" 命令执行失败!");
					inBr.close();
					in.close();
					return;
				}
			}
			inBr.close();
			in.close();

			File originFile = new File(filePath);
			File file = new File("C://temp.dex");
			FileInputStream fis = new FileInputStream(file);
			byte[] bytes = new byte[1000];
			if (fis.read(bytes) != -1) {
				StringBuilder sb = new StringBuilder();
				for (int i = 91; i > 87; i--) {
					sb.append(String.format("%02x",(bytes[i] & 255)));
				}
				System.out.println(originFile.getAbsolutePath() + " 方法数目 : "
						+ Integer.parseInt(sb.toString(), 16));
			}
			file.deleteOnExit();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 递归处理文件目录
	 * @param path
	 */
	public static void resolveDir(String path){

		File file=new File(path);
		File[] fileList = file.listFiles();
		for(int i = 0 ; i < fileList.length ; i++){
			if(fileList[i].isDirectory()){
				resolveDir(fileList[i].getAbsolutePath());
			}else{
				if(getFileType(fileList[i].getAbsolutePath()).equalsIgnoreCase(DEX)){
					resolveDex(fileList[i].getAbsolutePath());
				}else if(getFileType(fileList[i].getAbsolutePath()).equalsIgnoreCase(JAR)){
					resolveJar(fileList[i].getAbsolutePath());
				}

			}

		}
	}
}

  

时间: 2024-08-03 15:00:24

统计JAR包DEX文件中的方法数目的相关文章

《Java虚拟机原理图解》1.5、 class文件中的方法表集合--method方法在class文件中是怎样组织的

0. 前言 了解JVM虚拟机原理是每一个Java程序员修炼的必经之路.但是由于JVM虚拟机中有很多的东西讲述的比较宽泛,在当前接触到的关于JVM虚拟机原理的教程或者博客中,绝大部分都是充斥的文字性的描述,很难给人以形象化的认知,看完之后感觉还是稀里糊涂的. 感于以上的种种,我打算把我在学习JVM虚拟机的过程中学到的东西,结合自己的理解,总结成<Java虚拟机原理图解> 这个系列,以图解的形式,将抽象的JVM虚拟机的知识具体化,希望能够对想了解Java虚拟机原理的的Java程序员 提供点帮助.

《Java虚拟机原理图解》6、 class文件中的方法表集合--method方法在class文件中是怎样组织的

0. 前言 了解JVM虚拟机原理是每一个Java程序员修炼的必经之路.但是由于JVM虚拟机中有很多的东西讲述的比较宽泛,在当前接触到的关于JVM虚拟机原理的教程或者博客中,绝大部分都是充斥的文字性的描述,很难给人以形象化的认知,看完之后感觉还是稀里糊涂的. 感于以上的种种,我打算把我在学习JVM虚拟机的过程中学到的东西,结合自己的理解,总结成<Java虚拟机原理图解> 这个系列,以图解的形式,将抽象的JVM虚拟机的知识具体化,希望能够对想了解Java虚拟机原理的的Java程序员 提供点帮助.

java查找重复类/jar包/普通文件

开发web应用时,有时更新了类却没有生效,其实是因为jboss/tomcat中其他发布包下有同名类(包括全路径都相同). 于是萌发了做个程序来检查指定目录是否存在重复类(通过asm从类文件中取类的全路径),扩展开来,还支持查找重复的文件(按文件md5进行比较),重复的jar文件. 主要代码如下: 简单测试代码: package cn.jerryhouse.util.dup_files.test; import java.io.File; import org.junit.Test; import

不借助第三方jar包实现文件上传

如果实现文件上传难道非要借助第三方jar包(最常用的莫过于apache的commons-fileupload工具包)来实现吗?答案是否定的,下面通过例子演示在不借助第三方jar包的前提下如何实现文件的上传: 一.第一种实现方式 正在总结稍等!!! 二.第二种实现方式 正在总结稍等!!! 不借助第三方jar包实现文件上传,布布扣,bubuko.com

maven中手动将jar包安装进仓库的方法及问题

众所周知,我们只要在pom.xml文件中进行配置,maven就会自动下载jar包到本地仓库,那么,如果我们自己写一个jar包自己用,那么便无法通过配置来引用这个包,需要我们手动将包安装进仓库中. 我们使用命令mvn install:install-file  -Dfile=your-jar-file-path  -DgroupId=com.your.group  -DartifactId=your-artifactId -Dversion=x.x -Dpackaging=jar在doc中进行ja

android中打包含有Activity以及资源文件的jar包在工程中调用

研究如何将资源以及activity文件大包成jar文件供项目调用,走了不少弯路,也尝试了各种不同方式,都不太理想,最终还是从android的官方文档中找到了完美的解决,这里做一个总结. 最近刚刚发布了一款小应用<诗词大全>,有兴趣的朋友请实用提出您的宝贵意见,谢谢 这篇博文内容是结合android 开发文档的翻译以及个人在开发过程中的具体情况的一个总结 第一步,把普通的android project设置成库项目 库项目也是一个标准的android项目,因此你先创建一个普通的android项目.

Java读取jar包资源文件

把java项目打包成jar包,如果jar包中存在资源文件需要访问,需要采取stream的形式访问,可以调用getResourceAsStream()方法,而不能采用路径的方式访问(文件已经被打到jar里面了,不符合路径的).使用getResourceAsStream()方法文件可以有两种方式存放: (1)文件存放在src的类路径下. (2)文件存放在资源文件夹下面,通过Build Path-Use as Source Folder来把普通文件夹变成资源文件夹. 如图,把testA打包成jar包,

【Java】修改jar包class文件小记

今天被瑶瑶要求帮忙了 他现在用的软件里有一个bug,因为是外国的开发者,所以要求修改是很麻烦的事.要改的东西很简单,就是将一个函数里错误的返回值修改. 拿到手的有软件直接能运行的jar包. 一开始的想法比较简单,就想直接反编译代码,然后修改后重新打包,使用的是一直比较钟爱的jd-gui. 不过之后发现没有那么简单,由于这个软件功能比较多,代码比较繁琐,反编译之后代码依赖关系大量的报错,想要将所有报错都修改好再重新打包几乎是不可能的,只能放弃. 然后瑶瑶告诉我,他之前已经定位到了有bug的方法,只

03_dbcp数据源依赖jar包,DBCP中API介绍,不同过dbcp方式使用dbcp数据库连接池,通过配置文件使用dbcp数据库连接池

 DBCP数据源 使用DBCP数据源,需要导入两个jar包 Commons-dbcp.jar:连接池的实现 Common-pool.jar:连接池实现的依赖库. 导入mysql的jar包. DBCP核心API BasciDataSource   它可以通过实例化对象的方式获得一个对象. 它里面有如下方法: setDriverClassName(String driverClassName) 设置驱动类的名称. setInitialSize(int initialSize) 设置初始化时的链接