Android中未引用用的资源,如图片、anim文件、raw目录的文件、string目录的资源等等,可以通过lint找出来然后删除。这里可以参考两篇文章Android项目 删除未使用资源的方法 使用lint和android 删除未引用资源,优化字节码。但是在实际使用中还是出现了一些问题。
下面是我步骤。
//1 在andorid项目目录下运行如下命令,将未引用的资源重定向到unused.txt中。grep命令在windows下不存在,但是可以安装powerCMD或者github的windows客户端解决,或者,自己写java程序,可以参考上面的第一篇文章(没有实现) //lint .|grep "appears to be unused" > unused.txt 文件内容大致如下(可以发现一些规律) ----------------------- …… res/values/colors.xml:42: Warning: The resource R.color.deco_1 appears to be unused [UnusedResources] res/values/arrays.xml:512: Warning: The resource R.array.poster_format5 appears to be unused [UnusedResources] res/values/arrays.xml:529: Warning: The resource R.array.poster_format6 appears to be unused [UnusedResources] res/drawable-xlarge-mdpi/cloudlib_ani_uploading_02.png: Warning: The resource R.drawable.cloudlib_ani_uploading_02 appears to be unused [UnusedResources] res/values/colors.xml:30: Warning: The resource R.color.btn_green_press appears to be unused [UnusedResources] …… ----------------------- 但是有个问题是,我发现lint运行之后结果并不完整,也就是说执行真个过程之后最好在lint一次。 //2 根据上面文件的规律,删除单独文件(drawable,anim,layout等等) 输出drawable文件 //rm `awk -F: '{print $1}' unused.txt | sed -e 's/ *$//' |grep -E 'drawable'` 或者删除多种类型的文件 //rm `awk -F: '{print $1}' unused.txt | sed -e 's/ *$//' |grep -E 'png|jpg|res/layout|res/anim'` 注意这也是mac和linux种才有的(快一些程序删除或者安装软件)。注意mac版本和上面第二篇文章的命令可能有所不同。 但是问题来了,string、string-arring、color之类的资源就无能为力,因为他们都是在一个文件中的。 我觉得通过sed命令应该可以做到,但是我尝试很久,并没有成功,于是先用java提取这类资源,然后再通过sed命令删除这类资源。 //2 运行下面的程序 //代码如下 public class FileRowHander { public static void main(String[] args) { //unused.txt文件路径 String inpath = "/Users/XCL/Documents/android/project/rc/CollageMaker/unused.txt"; new FileRowHander(inpath).handleRow(); } private void write(String content) { //输出路径 String outpath = "/Users/XCL/Documents/android/project/rc/CollageMaker/out.txt"; BufferedWriter bw = null; try { bw = new BufferedWriter(new FileWriter(outpath)); } catch (IOException e) { System.err.println("文件创建失败"); e.printStackTrace(); return ; } try { bw.write(content) ; bw.flush(); } catch (IOException e) { System.err.println("文件写入失败"); e.printStackTrace(); } finally { try { bw.close(); } catch (IOException e) { System.err.println("文件关闭操作异常"); e.printStackTrace(); } } } String filepath = null ; public FileRowHander(String filepath) { this.filepath = filepath; } public void handleRow() { try { FileInputStream in = new FileInputStream(this.filepath); List<ResObj> list = process(in); StringBuffer sb = new StringBuffer(); for (ResObj resObj : list) { if(resObj.type != Type.ARRAY && resObj.type != Type.LAYOUT){ sb.append(resObj.toString()).append("\n"); } } write(sb.toString()); } catch (Exception e) { e.printStackTrace(); } } /** * * Warning: The resource R.drawable.roidapp_imagelib_btn_splite_right appears to be unused [UnusedResources] * Warning: The resource R.drawable.tab_selected_photogrid appears to be unused [UnusedResources] * * @param line */ private ResObj doHandle(String line) { ResObj resObj = null ; if(line.startsWith("res/layout")){ resObj = handleLayout(line); }else if(line.startsWith("res/drawable")) { resObj = handleDrawable(line); }else if(line.startsWith("res/anim")) { resObj = handleAnim(line); }else if(line.startsWith("res/values")) { if(line.contains("The resource R.string")){ resObj = handleString(line); }else if(line.contains("The resource R.dimen")) { resObj = handleDimen(line); }else if(line.contains("The resource R.color")) { resObj = handleColor(line); }else if(line.contains("The resource R.array")){ resObj = handleArray(line); } } if(resObj == null){ System.out.println("Not handle:"+line); } return resObj ; } //TODO //res/values/arrays.xml:134: Warning: The resource R.array.Banshi7 appears to be unused [UnusedResources] private ResObj handleArray(String line) { int indexOf = line.indexOf(':', 0); String path = line.substring(0, indexOf); String res = getResName(line,ARRAY_PATTERN); return new ResObj(path, res, Type.ARRAY); } static Pattern STRING_PATTERN = null ; static Pattern COLOR_PATTERN = null ; static Pattern DIMEN_PATTERN = null ; static Pattern ARRAY_PATTERN = null ; static Pattern DRAWABLE_PATTERN = null ; static Pattern ANIM_PATTERN = null ; static Pattern LAYOUT_PATTERN = null ; static { STRING_PATTERN = Pattern.compile("R.string.(.*) appears"); DRAWABLE_PATTERN = Pattern.compile("R.drawable.(.*) appears"); LAYOUT_PATTERN = Pattern.compile("R.layout.(.*) appears"); ANIM_PATTERN = Pattern.compile("R.anim.(.*) appears"); DIMEN_PATTERN = Pattern.compile("R.dimen.(.*) appears"); ARRAY_PATTERN = Pattern.compile("R.array.(.*) appears"); COLOR_PATTERN = Pattern.compile("R.color.(.*) appears"); } //res/layout/activity_main.xml: Warning: The resource R.layout.activity_main appears to be unused [UnusedResources] private ResObj handleLayout(String line) { int indexOf = line.indexOf(':', 0); String layoutPath = line.substring(0, indexOf); String resName = getResName(line, LAYOUT_PATTERN); return new ResObj(layoutPath, resName, Type.LAYOUT); } //res/drawable-xhdpi/bg_2_13_i.png: Warning: The resource R.drawable.bg_2_13_i appears to be unused [UnusedResources] //res/drawable-hdpi/beauty_ad_bg.9.png: Warning: The resource R.drawable.beauty_ad_bg appears to be unused [UnusedResources] private ResObj handleDrawable(String line) { int indexOf = line.indexOf(':', 0); String path = line.substring(0, indexOf); String resName = getResName(line, DRAWABLE_PATTERN); return new ResObj(path, resName, Type.LAYOUT); } //res/anim/waiting.xml: Warning: The resource R.anim.waiting appears to be unused [UnusedResources] private ResObj handleAnim(String line) { int indexOf = line.indexOf(':', 0); String path = line.substring(0, indexOf); String resName = getResName(line, ANIM_PATTERN); return new ResObj(path, resName, Type.ANIM); } //res/values/strings.xml:7: Warning: The resource R.string.roidapp_imagelib_filter_text appears to be unused [UnusedResources] private ResObj handleString(String line) { int indexOf = line.indexOf(':', 0); String path = line.substring(0, indexOf); String res = getResName(line,STRING_PATTERN); return new ResObj(path, res, Type.STRING); } //res/values/dimens.xml:4: Warning: The resource R.dimen.weibosdk_dialog_left_margin appears to be unused [UnusedResources] private ResObj handleDimen(String line) { int indexOf = line.indexOf(':', 0); String path = line.substring(0, indexOf); String res = getResName(line,DIMEN_PATTERN); return new ResObj(path, res, Type.DIMEN); } //res/values/colors.xml:14: Warning: The resource R.color.com_facebook_blue appears to be unused [UnusedResources] private ResObj handleColor(String line) { int indexOf = line.indexOf(':', 0); String path = line.substring(0, indexOf); String res = getResName(line,COLOR_PATTERN); return new ResObj(path, res, Type.COLOR); } private String getResName(String line,Pattern pattern) { String res = null ; Matcher matcher = pattern.matcher(line); System.out.println(matcher.groupCount()); if (matcher.find()) { res = matcher.group(1); System.out.println(res); } return res; } enum Type{ LAYOUT, ANIM , COLOR , DRAWABLE , DIMEN , ARRAY , STRING ; } class ResObj { String filepath ; String resName ; Type type ; public ResObj(String filepath, String resName, Type type) { super(); this.filepath = filepath; this.resName = resName; this.type = type; } @Override public String toString() { return filepath + ":"+resName + ":" + type.toString().toLowerCase() ; } } class ResObjExt extends ResObj { public ResObjExt(String filepath, String resName, Type type,int start,int end) { super(filepath, resName, type); } } //import org.apache.http.protocol.HTTP; private List<ResObj> process(InputStream is) throws IOException { List<ResObj> list = new ArrayList<ResObj>(); InputStreamReader isr = null ; BufferedReader reader = null ; try { isr = new InputStreamReader(is,"UTF-8"); //BufferedReader reader = new BufferedReader(new InputStreamReader(is,HTTP.UTF_8)); reader = new BufferedReader(isr); String line = null; String sep = System.getProperty("line.separator"); while ((line = reader.readLine()) != null) { if (!reader.ready()) {//http://stackoverflow.com/questions/4841925/java-io-ioexception-bufferedinputstream-is-closed-in-android-2-3 break; } ResObj obj = doHandle(line); if(obj != null){ list.add(obj); } } reader.close(); } catch (Exception e) { }finally { try { if(isr != null){ isr.close(); } if(reader != null){ reader.close(); } } catch (Exception e2) { } } return list ; } //import org.apache.http.protocol.HTTP; public static String read(InputStream is) throws IOException { StringBuffer contents = new StringBuffer(); //BufferedReader reader = new BufferedReader(new InputStreamReader(is,HTTP.UTF_8)); BufferedReader reader = new BufferedReader(new InputStreamReader(is,"UTF-8")); String line = null; String sep = System.getProperty("line.separator"); while ((line = reader.readLine()) != null) { contents.append(line + sep); if (!reader.ready()) {//http://stackoverflow.com/questions/4841925/java-io-ioexception-bufferedinputstream-is-closed-in-android-2-3 break; } } reader.close(); return contents.toString(); } } 生成文件(out.txt)内容大致如下(三列文件位置,名称和类型,以:分隔),注意,代码里可以确定资源类型(这里只是提取string目录下的资源,如color、string、array等)。 --------------------------------------------------- …… strings.xml:wangliang:string strings.xml:hello_world:string ……. --------------------------------------------------- //3 //运行shell脚本,通过sed删除资源所在行 //./do.sh out.txt --------------------------------------------------- #!/bin/bash for line in `cat $1` do read path res t <<<$(IFS=":"; echo $line) echo "sed path:" $path "res" $res sed -i "" "/name=\"$res\"/d" $path done --------------------------------------------------- 注意,对于array,这种方法会有问题,因为array一般都是多行的,还有string是多行的也会有问题,因为删除不彻底。但是刷新之后eclipse会报错,这样就手动删除,最后也是可以的。sed命令在windows中同样没有对应的,要么安装软件,要么写java程序(java好像没有直接修改文件的方法,可以打开输入流和输出流,依次从输入流中读取行,然后查看是否匹配,匹配则不添加到输出流中)。