扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
此工具在我的github上。地址:https://github.com/NashLegend/AndroidResourceCleaner
公司主营业务:成都网站设计、网站制作、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。成都创新互联公司是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。成都创新互联公司推出萨嘎免费做网站回馈大家。
很多人都知道androidunusedresources.jar这个工具,它可以把Android项目中无用的资源列出来。然而它所做的也就止于此了,在列出所有的无用资源以后,开发者们还得手动删除这些文件,这实在是一个没技术含量却又烦人的体力活,作为程序员,自然是有解决办法的,我们为什么不写一个程序,让程序来实现这个功能呢?有些人给出了部分解决方案,就是清除部分无用资源,比如layout和drawable,因为这种资源是和文件一一对应的,只要删除对应文件就可以实现资源的清理,但是对于其他没用到的如string、style等资源却没有处理。而本文的工具则弥补了这个缺点,可以清理所有的资源文件。
这个功能要实现的功能应该是这样的:
1、读取androidunusedresources.jar导出的无用资源列表。
2、清理无用的资源,包括删除无用文件以及修改包含无用资源的文件。
对于drawable和layout等资源,当然是直接删掉文件就行了,因为一个文件就对应着一个资源,而对于其他的资源就不是这么一回事了,一个文件里面可能有很多资源,所以只能是删除文件里面的一部分,而保留另一部分。
程序大体如下:
首先是main
public static void main(String[] args) { if (args.length > 0) { unusedCleaner(args[0]); } }
调用了unusedCleaner方法,我们就用这个方法清理无用资源,传入的参数是androidunusedresources.jar导出的无用资源文件列表。
然后看unusedCleaner方法。
// currents是在找到下一个文件前列出的所有资源id。因为一个文件可能对应着多个id,有可能是一个多对多的关系。 static ArrayListcurrents = new ArrayList<>(); static boolean LastIsPath = false; public static void unusedCleaner(String filePath) { ArrayList files = new ArrayList<>();//待清理资源列表 try { File file = new File(filePath);//androidunusedresources导出的无用资源文件列表 if (file.isFile() && file.exists()) { InputStreamReader read = new InputStreamReader( new FileInputStream(file), "utf-8"); BufferedReader bufferedReader = new BufferedReader(read); String lineTxt = null; while ((lineTxt = bufferedReader.readLine()) != null) { if (!parseType(lineTxt)) { //逐行读取数据,并提取数据。parseType方法可以判断是否此行代表发现了资源名。对应的格式如drawable: bg_list //如果不是的话,那么这一行就有可能是资源所对应的文件。 String trim = lineTxt.trim(); if (new File(trim).exists()) { //简单的用文件是否存在判断此行到底是不是一个文件名,如果是的话,说明currents列表中所有对应的资源名肯定包含在这个文件里 //就把它添加到一个待清理的资源列表中,将这些资源和文件对应起来,并添加到待清理列表中 for (Iterator iterator = currents .iterator(); iterator.hasNext();) { TypeSource typeSource = (TypeSource) iterator .next().clone(); typeSource.path = trim; typeSource.xmlTag = typeSource.getXmlTag(); files.add(typeSource); } LastIsPath = true; } else { continue; } } } read.close(); } else { System.out.println("noFile"); } } catch (Exception e) { System.out.println("Failed"); e.printStackTrace(); } //全部找出来后,将这些资源一一清理就是了,清理文件是TypeSource的方法,TypeSource类代表一个资源。 for (Iterator iterator = files.iterator(); iterator .hasNext();) { TypeSource typeSource = (TypeSource) iterator.next(); System.out.println(typeSource); typeSource.cleanSelf(); } System.out.println("done"); } public static boolean parseType(String lineTxt) { //判断当前行是不是一个资源名。如果是的话,加入到currents中,如果上一个资源是文件,那么清空currents,因为之前currents中的已经加入了待清理列表 String reg = "((drawable)|(layout)|(dimen)|(string)|(attr)|(style)|(styleable)|(color)|(id)|(anim))\\s*:\\s*(\\S+)"; Matcher matcher = Pattern.compile(reg).matcher(lineTxt);//使用正则匹配当前行 if (matcher.find()) { if (LastIsPath) { currents.clear(); } LastIsPath = false; TypeSource typeSource = new TypeSource(); typeSource.type = matcher.group(1); typeSource.name = matcher.group(12); currents.add(typeSource); return true; } else { return false; } } static class TypeSource { String type = "";// 类型 String name = "";// xml中的name属性 String xmlTag = "";// xml的tag名 String path = "";// 属于哪个文件 public String getXmlTag() { if ("styleable".equals(type)) { return "declare-styleable"; } else { return type; } } @Override public String toString() { return type + " | " + name + " | " + xmlTag + " | " + path; } /** * 一个一个的单独删,反正很快,就不考虑效率了。如果把一个文件对应的所有资源列出来统一删除应该很快,但是这里偷懒了 */ public void cleanSelf() { try { if (type == null) { return; } if (type.equals("drawable") || type.equals("layout") || type.equals("anim")) { new File(path).delete(); } else if (type.equals("id") || type.equals("")) { // 跳过了id资源,如果要删除的话,跟deleteNodeByName方法差不多。 } else { //deleteNodeByName方法删除xml中单个资源项。 deleteNodeByName(path, xmlTag, name); } } catch (Exception e) { } } public TypeSource clone() { TypeSource ts = new TypeSource(); ts.type = type; ts.name = name; ts.xmlTag = xmlTag; ts.path = path; return ts; } } @SuppressWarnings("unchecked") public static void deleteNodeByName(String path, String tag, String name) { //删除xml文件中的某个资源项 try { SAXReader reader = new SAXReader(); Document document = reader.read(new File(path)); Element element = document.getRootElement(); List list = element.elements(tag); for (int i = 0; i < list.size(); i++) { Element ele = list.get(i); String tName = ele.attributeValue("name"); if (tName != null && tName.length() > 0) { if (name.equals(ele.attributeValue("name"))) { element.remove(ele); break; } } } OutputFormat format = new OutputFormat("", false);// XMLWriter xmlWriter = new XMLWriter(new FileWriter(path), format); xmlWriter.write(document); xmlWriter.flush(); } catch (Exception e1) { e1.printStackTrace(); } }
然后导出成jar,使用方法如下
java -jar AndroidUnusedResources.jar >del.txt java -jar cleaner.jar del.txt
好了,原来几小时搞定的事现在只要几秒钟了。
由于被无用资源引用的资源不会被视为无用资源,所以要多执行几遍上面的命令才能真正清除掉
注意由于androidunusedresources导出的结果并不十分准确,有可能出错,所以会导致清理有可能不准确。可能会漏掉某些资源删除不了。这时候就得动手了。
最后,记得的清理资源前先把代码提交一下,你懂的
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流