一个自动清理Android项目无用资源的工具类

此工具在我的github上。地址:https://github.com/NashLegend/AndroidResourceCleaner

很多人都知道androidunusedresources.jar这个工具,它可以把Android项目中无用的资源列出来。然而它所做的也就止于此了,在列出所有的无用资源以后,开发者们还得手动删除这些文件,这实在是一个没技术含量却又烦人的体力活,但是作为程序员,自然是有解决办法的,我们为什么不写一个程序,让程序来实现这个功能呢?

这个功能要实现的功能应该是这样的:

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 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("dimen");
			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 unused.txt
java -jar cleaner.jar unused.txt

好了,原来几小时搞定的事现在只要几秒钟了。

由于被无用资源引用的资源不会被视为无用资源,所以要多执行几遍上面的命令才能真正清除掉

注意由于androidunusedresources导出的结果并不十分准确,有可能出错,所以会导致清理有可能不准确。可能会漏掉某些资源删除不了。这时候就得动手了。

最后,记得的清理资源前先把代码提交一下,你懂的

时间: 2025-01-02 15:56:14

一个自动清理Android项目无用资源的工具类的相关文章

【转载】Android应用框架及常用工具类总结

转载自:Android应用框架 http://www.tuicool.com/articles/feqmQj 常用工具类总结    http://blog.csdn.net/krislight/article/details/11354119 一. UML类图复习: UML类图中有些标记很容易混淆,这里先复习下,请大家看下面这幅图: 注:这幅图来自<大话设计模式>这本书中的插图. 二.应用框架: A.基本概念 抽象(抽出共同之现象)——在同领域的程序中,常含有许多类别,这些类别有其共同点,我们

15 友盟项目--资源文件工具类(ResourceUtil)、sql执行工具类(ExecSQLUtil)

资源文件工具类把sql脚本转换为String字符串--->交给sql工具类ExecSQLUtil执行sql 1.资源文件工具类(ResourceUtil) 把sql脚本转换为String字符串 /** * 资源文件工具类 */ public class ResourceUtil { /** * 以String方式读取整个资源串 */ public static String readResourceAsString(String resource ,String charset) throws

iOS开发项目篇—21抽取工具类

iOS开发项目篇—21抽取工具类 一.抽取宏 把和应用相关的信息抽取出来 App Key:1972915028 App Secret:b255603c4dfd82b4785bf9a808ce2662 回调地址:http://www.cnblogs.com/wendingding/ (1)appkey和回调页面在很多地方都要用到 (2)如果是不同应用的话,只需要把这几个参数换掉就可以了.把它们抽取成一个宏,写到pch文件中. 项目的PCH文件 1 #import <Availability.h>

实用篇:说说我在JavaScript项目中使用的工具类

在JavaScript的开发中,我们都会写一些工具类来帮我们简化一些业务操作的逻辑,一下就貼几个我在项目开发过程中常用的工具类.表达能力有限,各位看官还是看源码吧. 一.日期处理工具类. /** * 日期处理工具类 * @Authors: jackyWHJ * @date 2013-10-18 * */ var DateUtils = { /** * 得到日期在一年当中的周数 */ getISOYearWeek: function(date) { var commericalyear = thi

封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil

封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil,代码比较简单,主要是把MongoTarget的配置.FileTarget的配置集成到类中,同时利用缓存依赖来判断是否需要重新创建Logger类,完整代码如下: using NLog; using NLog.Config; using NLog.Mongo; using NLog.Targets; using System; using System.Collections.Generic; using System.IO;

重复造轮子,编写一个轻量级的异步写日志的实用工具类(LogAsyncWriter)

一说到写日志,大家可能推荐一堆的开源日志框架,如:Log4Net.NLog,这些日志框架确实也不错,比较强大也比较灵活,但也正因为又强大又灵活,导致我们使用他们时需要引用一些DLL,同时还要学习各种用法及配置文件,这对于有些小工具.小程序.小网站来说,有点“杀鸡焉俺用牛刀”的感觉,而且如果对这些日志框架不了解,可能输出来的日志性能或效果未毕是与自己所想的,鉴于这几个原因,我自己重复造轮子,编写了一个轻量级的异步写日志的实用工具类(LogAsyncWriter),这个类还是比较简单的,实现思路也很

项目中js的工具类

js工具类的功能有: 1.去掉字符串前后空格 2.清空select 3.验证手机号 4.字符串转换int型数字 5.获取checkbox的选中的值 6.去掉左边的空白 7.去掉邮编的空白 源码如下: /** * 去掉字符串前后空格 * * @param str * @returns */ function trim(str){ return str.replace(/^(\s|\xA0)+|(\s|\xA0)+$/g, ''); } /** * 清空select * * @param selec

如何在使用eclipse的情况下,清理android项目中的冗余class文件和资源文件以及冗余图片

在我们迭代项目的过程中,经常会启用某些功能,或者修改某些界面的问题,那么问题来了,这样很容易出现大量的冗余.java文件,冗余资源文件,一些冗余的界面文件等.那么问题既然出现了,那么如何去解决呢,这就是今天着重要去解决的问题? first: eclipse有个检查冗余java文件的插件,名叫UCDetector: 下载地址为:http://sourceforge.net/projects/ucdetector/files/latest/download?source=files 官网地址:htt

一键删除android下面无用资源

项目需求一改再改,UI一调再调,结果就是项目中一堆已经用不到但却没有清理的垃圾资源,不说工程大小问题,对新进入项目的人或看其他模块的代码的人来说,这些没清理的资源可能也可能会带来困扰,所以最好还是清理掉这些垃圾,对于一个稍微大一点的工程来说,手工清理明显是不现实的,这就需要一个方法做这些事情. 本人最怕码字,上面内容引入http://www.cnblogs.com/angeldevil/p/3725358.html 关于android lint的使用,如果不了解的请自行去了解. 下面是我的清除代