统计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()); } } } } }