此工具在個人github上。地址:https://github.com/NashLegend/AndroidResourceCleaner java
不少人都知道androidunusedresources.jar這個工具,它能夠把Android項目中無用的資源列出來。然而它所作的也就止於此了,在列出全部的無用資源之後,開發者們還得手動刪除這些文件,這實在是一個沒技術含量卻又煩人的體力活,做爲程序員,天然是有解決辦法的,咱們爲何不寫一個程序,讓程序來實現這個功能呢?有些人給出了部分解決方案,就是清除部分無用資源,好比layout和drawable,由於這種資源是和文件一一對應的,只要刪除對應文件就能夠實現資源的清理,可是對於其餘沒用到的如string、style等資源卻沒有處理。而本文的工具則彌補了這個缺點,能夠清理全部的資源文件。android
這個功能要實現的功能應該是這樣的:git
一、讀取androidunusedresources.jar導出的無用資源列表。程序員
二、清理無用的資源,包括刪除無用文件以及修改包含無用資源的文件。github
對於drawable和layout等資源,固然是直接刪掉文件就好了,由於一個文件就對應着一個資源,而對於其餘的資源就不是這麼一回事了,一個文件裏面可能有不少資源,因此只能是刪除文件裏面的一部分,而保留另外一部分。
ide
程序大致以下:工具
首先是mainspa
public static void main(String[] args) { if (args.length > 0) { unusedCleaner(args[0]); } }
調用了unusedCleaner方法,咱們就用這個方法清理無用資源,傳入的參數是androidunusedresources.jar導出的無用資源文件列表。orm
而後看unusedCleaner方法。xml
// currents是在找到下一個文件前列出的全部資源id。由於一個文件可能對應着多個id,有多是一個多對多的關係。 static ArrayList<TypeSource> currents = new ArrayList<>(); static boolean LastIsPath = false; public static void unusedCleaner(String filePath) { ArrayList<TypeSource> 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<TypeSource> 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<TypeSource> 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<Element> 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導出的結果並不十分準確,有可能出錯,因此會致使清理有可能不許確。可能會漏掉某些資源刪除不了。這時候就得動手了。
最後,記得的清理資源前先把代碼提交一下,你懂的