Lint删除未引用的资源

Android中未引用用的资源,如图片、anim文件、raw目录的文件、string目录的资源等等,可以通过lint找出来然后删除。这里可以参考两篇文章Android项目 删除未使用资源的方法 使用lintandroid 删除未引用资源,优化字节码。但是在实际使用中还是出现了一些问题。


下面是我步骤。

//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好像没有直接修改文件的方法,可以打开输入流和输出流,依次从输入流中读取行,然后查看是否匹配,匹配则不添加到输出流中)。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值