android多余资源自动清理工具arc发布到google code

1. 我已经将arc(android resource cleaner)的3.0 beta版本的源代码发布到google code,项目目前还很粗鄙,需要进一步的完善,里面也可能还有不少bug,需要解决,感兴趣的朋友可以联系我,里面有我的qq号码(286505491).

2. 项目所在地址: https://android-resource-cleaner.googlecode.com/svn/trunk/

3.项目里面两个主要的类:

ARC.java   主要负责UI和事件处理

package xiaogang.src;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.SwingUtilities;

class ARC extends JFrame {
    private static final String VERSION_STR = "3.0 beta";
    private static final long serialVersionUID = -1600532762007579488L;

    private JMenu mFileMenu;
    private JMenu mActionMenu;
    private JMenu mHelpMenu;

    private JMenuItem mOpenFileMenuItem;
    private JMenuItem mDeleteMenuItem;
    private JMenuItem mDeleteAllMenuItem;
    private JMenuItem mExitMenuItem;
    private JMenuItem mHelpMenuItem;

    private JButton mJButton;
    private JMenuBar mJMenuBar1;
    private JList mUnusedResList;
    private JSeparator mJSeparator1, mJSeparator2;

    private ResProbe mCleanRes;

    private String mSPath;

    private boolean running = false;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                ARC inst = new ARC();
                inst.setLocationRelativeTo(null);
                inst.setVisible(true);
            }
        });
    }

    public ARC() {
        super();
        setupGUI();
    }

    private void setupGUI() {
        try {
            BorderLayout thisLayout = new BorderLayout();
            getContentPane().setLayout(thisLayout);

            mUnusedResList = new JList();
            mUnusedResList.setListData(new String[] {
                    "请打开Android工程根目录"
            });
            mUnusedResList.setLayoutOrientation(JList.VERTICAL);
            JScrollPane listScroller = new JScrollPane(mUnusedResList);
            getContentPane().add(listScroller, BorderLayout.CENTER);

            mJButton = new JButton("开始");
            mJButton.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent arg0) {
                    if (running) {
                        mCleanRes.setbCancel(true);
                        mJButton.setText("开始");
                        running = false;
                        return;
                    }
                    if (mSPath == null && mSPath.length() <= 0) {
                        JOptionPane.showMessageDialog(ARC.this, "选择目录", "Error",
                                JOptionPane.ERROR_MESSAGE);
                    }

                    running = true;
                    mJButton.setText("取消");

                    new Thread() {

                        @Override
                        public void run() {
                            mCleanRes.run(ARC.this);
                        }
                    }.start();
                }
            });
            getContentPane().add(mJButton, BorderLayout.SOUTH);

            setSize(400, 300);
            {
                mJMenuBar1 = new JMenuBar();
                setJMenuBar(mJMenuBar1);
                {
                    mFileMenu = new JMenu();
                    mJMenuBar1.add(mFileMenu);
                    mFileMenu.setText("文件");
                    {
                        mOpenFileMenuItem = new JMenuItem();
                        mFileMenu.add(mOpenFileMenuItem);
                        mOpenFileMenuItem.setText("打开");
                        mOpenFileMenuItem.addActionListener(new ActionListener() {

                            @Override
                            public void actionPerformed(ActionEvent e) {
                                if (e.getActionCommand().equals("打开")) {
                                    JFileChooser jFileChooser = new JFileChooser();
                                    jFileChooser
                                    .setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                                    int returnVal = jFileChooser.showOpenDialog(ARC.this);
                                    if (returnVal == JFileChooser.APPROVE_OPTION) {
                                        mSPath = jFileChooser.getSelectedFile().getAbsolutePath();
                                        mCleanRes = new ResProbe(mSPath);
                                        mUnusedResList.setListData(new String[] {
                                                "工程目录: "+mSPath
                                        });
                                        mUnusedResList.invalidate();
                                    }
                                }
                            }
                        });
                    }
                    {
                        mJSeparator1 = new JSeparator();
                        mFileMenu.add(mJSeparator1);
                    }
                    {
                        mExitMenuItem = new JMenuItem();
                        mFileMenu.add(mExitMenuItem);
                        mExitMenuItem.setText("退出");
                        mExitMenuItem.addActionListener(new ActionListener() {

                            @Override
                            public void actionPerformed(ActionEvent arg0) {
                                ARC.this.dispose();
                            }
                        });
                    }
                }

                mActionMenu = new JMenu();
                mJMenuBar1.add(mActionMenu);
                mActionMenu.setText("操作");

                mDeleteMenuItem = new JMenuItem();
                mActionMenu.add(mDeleteMenuItem);
                mDeleteMenuItem.setText("删除");
                mDeleteMenuItem.addActionListener(new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent arg0) {
                        mCleanRes.delete(mUnusedResList.getSelectedIndices());
                        mUnusedResList.setListData(mCleanRes.getResult());
                        mUnusedResList.invalidate();
                    }

                });
                mJSeparator2 = new JSeparator();
                mActionMenu.add(mJSeparator2);
                mDeleteAllMenuItem = new JMenuItem();
                mActionMenu.add(mDeleteAllMenuItem);
                mDeleteAllMenuItem.setText("删除所有");
                mDeleteAllMenuItem.addActionListener(new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent arg0) {
                        mCleanRes.deleteAll();
                        mUnusedResList.setListData(mCleanRes.getResult());
                        mUnusedResList.invalidate();
                    }
                });

                mHelpMenu = new JMenu();
                mJMenuBar1.add(mHelpMenu);
                mHelpMenu.setText("帮助");

                mHelpMenuItem = new JMenuItem();
                mHelpMenu.add(mHelpMenuItem);
                mHelpMenuItem.setText("帮助");
                mHelpMenuItem.addActionListener(new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent arg0) {
                        JOptionPane.showMessageDialog(ARC.this, "Version v" + VERSION_STR
                                + "\n赵小刚,  QQ: 286505491", "ARC",
                                JOptionPane.INFORMATION_MESSAGE);
                    }
                });
            }
            this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void setResult(List<ResSet> list) {
        running = false;
        mJButton.setText("开始");
        String[] fileList = new String[list.size()];
        for (int i = 0; i < list.size(); i++) {
            fileList[i] = list.get(i).toString();
        }
        if (fileList.length == 0) {
            fileList = new String[1];
            fileList[0] = "No result";
        }
        mUnusedResList.setListData(fileList);
        mUnusedResList.invalidate();
    }

    public void setProgress(File file) {
        String[] pathList = new String[3];
        pathList[0] = "正在处理... : " + mSPath;
        pathList[1] = file.getParent().replace(mSPath, "");
        pathList[2] = file.getName();
        mUnusedResList.setListData(pathList);
        mUnusedResList.invalidate();
    }
}

ResProbe.java  负责扫描各种资源文件,源代码,r文件,清单文件,然后经过对比,找出冗余资源的核心类

package xiaogang.src;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class ResProbe {
    private volatile boolean isCanceled = false;
    private String mPackageName;
    private ARC mCallback;

    private File mBaseDirectory;
    private File mSrcDirectory;
    private File mResDirectory;
    private File mGenDirectory;
    private File mManifestFile;
    private File mRJavaFile;

    private List<ResSet> mResList = new ArrayList<ResSet>();
    private final Set<ResSet> mResources = new HashSet<ResSet>();
    private final Set<ResSet> mUsedResources = new HashSet<ResSet>();

    private static final Pattern sResourceTypePattern = Pattern
            .compile("^\\s*public static final class (\\w+)\\s*\\{$");
    private static final Pattern sResourceNamePattern = Pattern
            .compile("^\\s*public static( final)? int(\\[\\])? (\\w+)\\s*=\\s*(\\{|(0x)?[0-9A-Fa-f]+;)\\s*$");
    private static final FileType sJavaFileType = new FileType("java", "R." + FileType.USAGE_TYPE
            + "." + FileType.USAGE_NAME + "[^\\w_]");
    private static final FileType sXmlFileType = new FileType("xml", "[\" >]@"
            + FileType.USAGE_TYPE + "/" + FileType.USAGE_NAME + "[\" <]");

    private static final Map<String, ResType> sResourceTypes = new HashMap<String, ResType>();

    static {
        // anim
        sResourceTypes.put("anim", new ResType("anim") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals(getType())) {
                    return false;
                }

                final String name = fileName.split("\\.")[0];
                final Pattern pattern = Pattern.compile("^" + resourceName + "$");
                return pattern.matcher(name).find();
            }
        });

        // array
        sResourceTypes.put("array", new ResType("array") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals("values")) {
                    return false;
                }

                final Pattern pattern = Pattern.compile("<([a-z]+\\-)?array.*?name\\s*=\\s*\""
                        + resourceName + "\".*?/?>");

                final Matcher matcher = pattern.matcher(fileContents);

                if (matcher.find()) {
                    return true;
                }

                return false;
            }
        });

        // attr
        sResourceTypes.put("attr", new ResType("attr") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals("values")) {
                    return false;
                }

                final Pattern pattern = Pattern.compile("<attr.*?name\\s*=\\s*\"" + resourceName
                        + "\".*?/?>");

                final Matcher matcher = pattern.matcher(fileContents);

                if (matcher.find()) {
                    return true;
                }

                return false;
            }

            @Override
            public boolean doesFileUseResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (parent != null) {
                    if (!parent.isDirectory()) {
                        return false;
                    }

                    final String directoryType = parent.getName().split("-")[0];
                    if (!directoryType.equals("layout") && !directoryType.equals("values")) {
                        return false;
                    }
                }

                final Pattern pattern = Pattern.compile("<.+?:" + resourceName
                        + "\\s*=\\s*\".*?\".*?/?>");

                final Matcher matcher = pattern.matcher(fileContents);

                if (matcher.find()) {
                    return true;
                }

                final Pattern itemPattern = Pattern.compile("<item.+?name\\s*=\\s*\""
                        + resourceName + "\".*?>");
                final Matcher itemMatcher = itemPattern.matcher(fileContents);

                if (itemMatcher.find()) {
                    return true;
                }

                return false;
            }
        });

        // bool
        sResourceTypes.put("bool", new ResType("bool") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals("values")) {
                    return false;
                }

                final Pattern pattern = Pattern.compile("<bool.*?name\\s*=\\s*\"" + resourceName
                        + "\".*?/?>");

                final Matcher matcher = pattern.matcher(fileContents);

                if (matcher.find()) {
                    return true;
                }

                return false;
            }
        });

        // color
        sResourceTypes.put("color", new ResType("color") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals("values")) {
                    return false;
                }

                final Pattern pattern = Pattern.compile("<color.*?name\\s*=\\s*\"" + resourceName
                        + "\".*?/?>");

                final Matcher matcher = pattern.matcher(fileContents);

                if (matcher.find()) {
                    return true;
                }

                return false;
            }
        });

        // dimen
        sResourceTypes.put("dimen", new ResType("dimen") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals("values")) {
                    return false;
                }

                final Pattern pattern = Pattern.compile("<dimen.*?name\\s*=\\s*\"" + resourceName
                        + "\".*?/?>");

                final Matcher matcher = pattern.matcher(fileContents);

                if (matcher.find()) {
                    return true;
                }

                return false;
            }
        });

        // drawable
        sResourceTypes.put("drawable", new ResType("drawable") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (directoryType.equals(getType())) {

                    final String name = fileName.split("\\.")[0];
                    final Pattern pattern = Pattern.compile("^" + resourceName + "$");
                    return pattern.matcher(name).find();
                }

                if (directoryType.equals("values")) {
                    final Pattern pattern = Pattern.compile("<drawable.*?name\\s*=\\s*\""
                            + resourceName + "\".*?/?>");
                    final Matcher matcher = pattern.matcher(fileContents);
                    if (matcher.find()) {
                        return true;
                    }
                }

                return false;
            }
        });

        // id
        sResourceTypes.put("id", new ResType("id") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals("values") && !directoryType.equals("layout")) {
                    return false;
                }

                final Pattern valuesPattern0 = Pattern
                        .compile("<item.*?type\\s*=\\s*\"id\".*?name\\s*=\\s*\"" + resourceName
                                + "\".*?/?>");
                final Pattern valuesPattern1 = Pattern.compile("<item.*?name\\s*=\\s*\""
                        + resourceName + "\".*?type\\s*=\\s*\"id\".*?/?>");
                final Pattern layoutPattern = Pattern.compile(":id\\s*=\\s*\"@\\+id/"
                        + resourceName + "\"");
                Matcher matcher = valuesPattern0.matcher(fileContents);

                if (matcher.find()) {
                    return true;
                }

                matcher = valuesPattern1.matcher(fileContents);

                if (matcher.find()) {
                    return true;
                }

                matcher = layoutPattern.matcher(fileContents);

                if (matcher.find()) {
                    return true;
                }

                return false;
            }
        });

        // integer
        sResourceTypes.put("integer", new ResType("integer") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals("values")) {
                    return false;
                }

                final Pattern pattern = Pattern.compile("<integer.*?name\\s*=\\s*\"" + resourceName
                        + "\".*?/?>");
                final Matcher matcher = pattern.matcher(fileContents);
                if (matcher.find()) {
                    return true;
                }

                return false;
            }
        });

        // layout
        sResourceTypes.put("layout", new ResType("layout") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals(getType())) {
                    return false;
                }

                final String name = fileName.split("\\.")[0];

                final Pattern pattern = Pattern.compile("^" + resourceName + "$");

                return pattern.matcher(name).find();
            }
        });

        // menu
        sResourceTypes.put("menu", new ResType("menu") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals(getType())) {
                    return false;
                }

                final String name = fileName.split("\\.")[0];
                final Pattern pattern = Pattern.compile("^" + resourceName + "$");
                return pattern.matcher(name).find();
            }
        });

        // plurals
        sResourceTypes.put("plurals", new ResType("plurals") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals("values")) {
                    return false;
                }

                final Pattern pattern = Pattern.compile("<plurals.*?name\\s*=\\s*\"" + resourceName
                        + "\".*?/?>");
                final Matcher matcher = pattern.matcher(fileContents);
                if (matcher.find()) {
                    return true;
                }

                return false;
            }
        });

        // raw
        sResourceTypes.put("raw", new ResType("raw") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals(getType())) {
                    return false;
                }

                final String name = fileName.split("\\.")[0];
                final Pattern pattern = Pattern.compile("^" + resourceName + "$");
                return pattern.matcher(name).find();
            }
        });

        // string
        sResourceTypes.put("string", new ResType("string") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals("values")) {
                    return false;
                }

                final Pattern pattern = Pattern.compile("<string.*?name\\s*=\\s*\"" + resourceName
                        + "\".*?/?>");
                final Matcher matcher = pattern.matcher(fileContents);
                if (matcher.find()) {
                    return true;
                }

                return false;
            }
        });

        // style
        sResourceTypes.put("style", new ResType("style") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals("values")) {
                    return false;
                }

                final Pattern pattern = Pattern.compile("<style.*?name\\s*=\\s*\"" + resourceName
                        + "\".*?/?>");
                final Matcher matcher = pattern.matcher(fileContents);
                if (matcher.find()) {
                    return true;
                }
                return false;
            }

            @Override
            public boolean doesFileUseResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (parent != null) {
                    if (!parent.isDirectory()) {
                        return false;
                    }

                    final String directoryType = parent.getName().split("-")[0];
                    if (!directoryType.equals("values")) {
                        return false;
                    }
                }

                // (name="Parent.Child")
                final Pattern pattern = Pattern.compile("<style.*?name\\s*=\\s*\"" + resourceName
                        + "\\.\\w+\".*?/?>");

                final Matcher matcher = pattern.matcher(fileContents);

                if (matcher.find()) {
                    return true;
                }

                // (parent="Parent")
                final Pattern pattern1 = Pattern.compile("<style.*?parent\\s*=\\s*\""
                        + resourceName + "\".*?/?>");
                final Matcher matcher1 = pattern1.matcher(fileContents);
                if (matcher1.find()) {
                    return true;
                }
                return false;
            }
        });

        // styleable
        sResourceTypes.put("styleable", new ResType("styleable") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals("values")) {
                    return false;
                }

                final String[] styleableAttr = resourceName.split("\\[_\\\\.\\]");

                if (styleableAttr.length == 1) {

                    final Pattern pattern = Pattern.compile("<declare-styleable.*?name\\s*=\\s*\""
                            + styleableAttr[0] + "\".*?/?>");
                    final Matcher matcher = pattern.matcher(fileContents);

                    if (matcher.find()) {
                        return true;
                    }

                    return false;
                }

                final Pattern blockPattern = Pattern.compile("<declare-styleable.*?name\\s*=\\s*\""
                        + styleableAttr[0] + "\".*?>(.*?)</declare-styleable\\s*>");
                final Matcher blockMatcher = blockPattern.matcher(fileContents);

                if (blockMatcher.find()) {
                    final String styleableAttributes = blockMatcher.group(1);

                    final Pattern attributePattern = Pattern.compile("<attr.*?name\\s*=\\s*\""
                            + styleableAttr[1] + "\".*?/?>");
                    final Matcher attributeMatcher = attributePattern.matcher(styleableAttributes);
                    if (attributeMatcher.find()) {
                        return true;
                    }
                    return false;
                }

                return false;
            }
        });

        // xml
        sResourceTypes.put("xml", new ResType("xml") {
            @Override
            public boolean doesFileDeclareResource(final File parent, final String fileName,
                    final String fileContents, final String resourceName) {
                if (!parent.isDirectory()) {
                    return false;
                }

                final String directoryType = parent.getName().split("-")[0];
                if (!directoryType.equals(getType())) {
                    return false;
                }

                final String name = fileName.split("\\.")[0];
                final Pattern pattern = Pattern.compile("^" + resourceName + "$");
                return pattern.matcher(name).find();
            }
        });
    }

    public ResProbe() {
        super();
        final String baseDirectory = System.getProperty("user.dir");
        mBaseDirectory = new File(baseDirectory);
    }

    protected ResProbe(final String baseDirectory) {
        super();
        mBaseDirectory = new File(baseDirectory);
    }

    public void delete(int[] list) {
        LinkedList<ResSet> remove = new LinkedList<ResSet>();
        for (int i : list) {
            ResSet result = mResList.get(i);
            final String type = result.getType();
            System.out.println("type: "+type);
            if (type.equals("anim") || type.equals("drawable") || type.equals("layout")) {
                final String path = result.getPath();
                System.out.println("path: "+path);
                if (!isEmpty(path)) {
                    File file = new File(path);
                    file.delete();
                    remove.add(result);
                }
            }
        }
        mResList.removeAll(remove);
    }

    public String[] getResult() {
        String[] result = new String[mResList.size()];
        for (int i = 0; i < mResList.size(); i++) {
            result[i] = mResList.get(i).toString();
        }
        return result;
    }

    public void deleteAll() {
        LinkedList<ResSet> remove = new LinkedList<ResSet>();
        for (ResSet resource : mResList) {
            final String type = resource.getType();
            if (type.equals("anim") || type.equals("drawable") || type.equals("layout") ||type.equals("menu")) {
                final String path = resource.getPath();
                if (!isEmpty(path)) {
                    File file = new File(path);
                    file.delete();
                    remove.add(resource);
                }
            }
        }
        mResList.removeAll(remove);
    }

    private boolean isEmpty(CharSequence str) {
        if (str == null || str.length() == 0)
            return true;
        else
            return false;
    }

    public void showResult() {
        if (mCallback != null) {
            mCallback.setResult(mResList);
        }
    }

    @Override
    protected void finalize() throws Throwable {
        if (mResList != null) {
            mResList.clear();
        }
        super.finalize();
    }

    public void run(ARC callback) {

        isCanceled = false;

        if (callback != null) {
            this.mCallback = callback;
        }

        System.out.println("Running in: " + mBaseDirectory.getAbsolutePath());

        findPaths();

        if (mSrcDirectory == null || mResDirectory == null || mManifestFile == null) {
            System.err.println("The current directory is not a valid Android project root.");
            return;
        }

        mPackageName = findPackageName(mManifestFile);

        if (mPackageName == null || mPackageName.trim().length() == 0) {
            return;
        }

        if (mGenDirectory == null) {
            System.err.println("You must first build your project to generate R.java");
            return;
        }

        mRJavaFile = findRJavaFile(mGenDirectory, mPackageName);

        if (mRJavaFile == null) {
            System.err.println("You must first build your project to generate R.java");
            return;
        }

        mResources.clear();

        try {
            mResources.addAll(getResourceList(mRJavaFile));//查找R文件
        } catch (final IOException e) {
            System.err.println("The R.java found could not be opened.");
            e.printStackTrace();
        }

        if (isCanceled) {
            return;
        }

        System.out.println(mResources.size() + " resources found");
        System.out.println();

        mUsedResources.clear();

        searchFiles(null, mSrcDirectory, sJavaFileType);
        if (isCanceled) {
            return;
        }
        searchFiles(null, mResDirectory, sXmlFileType);
        if (isCanceled) {
            return;
        }
        searchFiles(null, mManifestFile, sXmlFileType);
        if (isCanceled) {
            return;
        }

        /*
         * Because attr and styleable are so closely linked, we need to do some
         * matching now to ensure we don't say an attr is unused if its
         * corresponding styleable is used.
         */
        final Set<ResSet> extraUsedResources = new HashSet<ResSet>();

        for (final ResSet resource : mResources) {
            if (resource.getType().equals("styleable")) {
                final String[] styleableAttr = resource.getName().split("_");

                if (styleableAttr.length > 1) {
                    final String attrName = styleableAttr[1];

                    final ResSet attrResourceTest = new ResSet("attr", attrName);

                    if (mUsedResources.contains(attrResourceTest)) {
                        extraUsedResources.add(resource);
                    }
                }
            } else if (resource.getType().equals("attr")) {
                for (final ResSet usedResource : mUsedResources) {
                    if (usedResource.getType().equals("styleable")) {
                        final String[] styleableAttr = usedResource.getName().split("_");

                        if (styleableAttr.length > 1 && styleableAttr[1].equals(resource.getName())) {
                            extraUsedResources.add(resource);
                        }
                    }
                }
            }
        }

        for (final ResSet resource : extraUsedResources) {
            mResources.remove(resource);
            mUsedResources.add(resource);
        }

        final SortedMap<String, SortedMap<String, ResSet>> unusedResources = new TreeMap<String, SortedMap<String, ResSet>>();

        for (final ResSet resource : mResources) {
            final String type = resource.getType();
            SortedMap<String, ResSet> typeMap = unusedResources.get(type);

            if (typeMap == null) {
                typeMap = new TreeMap<String, ResSet>();
                unusedResources.put(type, typeMap);
            }

            typeMap.put(resource.getName(), resource);
        }

        final Map<String, ResType> unusedResourceTypes = new HashMap<String, ResType>(
                unusedResources.size());

        for (final String type : unusedResources.keySet()) {
            final ResType resourceType = sResourceTypes.get(type);
            if (resourceType != null) {
                unusedResourceTypes.put(type, resourceType);
            }
        }

        findDeclaredPaths(null, mResDirectory, unusedResourceTypes, unusedResources);

        /*
         * Find the paths where the used resources are declared.
         */
        final SortedMap<String, SortedMap<String, ResSet>> usedResources = new TreeMap<String, SortedMap<String, ResSet>>();

        for (final ResSet resource : mUsedResources) {
            final String type = resource.getType();
            SortedMap<String, ResSet> typeMap = usedResources.get(type);

            if (typeMap == null) {
                typeMap = new TreeMap<String, ResSet>();
                usedResources.put(type, typeMap);
            }

            typeMap.put(resource.getName(), resource);
        }

        if (isCanceled) {
            return;
        }

        final Map<String, ResType> usedResourceTypes = new HashMap<String, ResType>(
                usedResources.size());

        for (final String type : usedResources.keySet()) {
            final ResType resourceType = sResourceTypes.get(type);
            if (resourceType != null) {
                usedResourceTypes.put(type, resourceType);
            }
        }

        if (isCanceled) {
            return;
        }

        findDeclaredPaths(null, mResDirectory, usedResourceTypes, usedResources);

        final Set<ResSet> libraryProjectResources = getLibraryProjectResources();

        for (final ResSet libraryResource : libraryProjectResources) {
            final SortedMap<String, ResSet> typedResources = unusedResources.get(libraryResource
                    .getType());

            if (typedResources != null) {
                final ResSet appResource = typedResources.get(libraryResource.getName());

                if (appResource != null && appResource.hasNoDeclaredPaths()) {
                    typedResources.remove(libraryResource.getName());
                    mResources.remove(appResource);
                }
            }
        }

        if (isCanceled) {
            return;
        }

        final int unusedResourceCount = mResources.size();

        if (unusedResourceCount > 0) {
            System.out.println(unusedResourceCount + " unused resources were found:");

            final SortedSet<ResSet> sortedResources = new TreeSet<ResSet>(mResources);

            for (final ResSet resource : sortedResources) {
                System.out.println(resource);
            }
            mResList.addAll(sortedResources);
        }

        showResult();
    }

    private void findPaths() {
        final File[] children = mBaseDirectory.listFiles();

        if (children == null) {
            return;
        }

        for (final File file : children) {
            if (file.isDirectory()) {
                if (file.getName().equals("src")) {
                    mSrcDirectory = file;
                } else if (file.getName().equals("res")) {
                    mResDirectory = file;
                } else if (file.getName().equals("gen")) {
                    mGenDirectory = file;
                }
            } else if (file.getName().equals("AndroidManifest.xml")) {
                mManifestFile = file;
            }
        }
    }

    private static String findPackageName(final File androidManifestFile) {
        String manifest = "";

        try {
            manifest = FileUtils.getFileContents(androidManifestFile);
        } catch (final IOException e) {
            e.printStackTrace();
        }

        final Pattern pattern = Pattern
                .compile("<manifest\\s+.*?package\\s*=\\s*\"([A-Za-z0-9_\\.]+)\".*?>");
        final Matcher matcher = pattern.matcher(manifest);

        if (matcher.find()) {
            return matcher.group(1);
        }

        return null;
    }

    private static File findRJavaFile(final File baseDirectory, final String packageName) {
        final File rJava = new File(baseDirectory, packageName.replace('.', '/') + "/R.java");

        if (rJava.exists()) {
            return rJava;
        }

        return null;
    }

    public void setbCancel(boolean bCancel) {
        this.isCanceled = bCancel;
    }

    /**
     * Removes all resources declared in library projects.
     */
    private Set<ResSet> getLibraryProjectResources() {
        final Set<ResSet> resources = new HashSet<ResSet>();

        final File projectPropertiesFile = new File(mBaseDirectory, "project.properties");

        if (!projectPropertiesFile.exists()) {
            return resources;
        }

        List<String> fileLines = new ArrayList<String>();
        try {
            fileLines = FileUtils.getFileLines(projectPropertiesFile);
        } catch (final IOException e) {
            e.printStackTrace();
        }

        final Pattern libraryProjectPattern = Pattern.compile(
                "^android\\.library\\.reference\\.\\d+=(.*)$", Pattern.CASE_INSENSITIVE);

        final List<String> libraryProjectPaths = new ArrayList<String>();

        for (final String line : fileLines) {
            final Matcher libraryProjectMatcher = libraryProjectPattern.matcher(line);

            if (libraryProjectMatcher.find()) {
                libraryProjectPaths.add(libraryProjectMatcher.group(1));
            }
        }

        for (final String libraryProjectPath : libraryProjectPaths) {
            final File libraryProjectDirectory = new File(mBaseDirectory, libraryProjectPath);

            if (libraryProjectDirectory.exists() && libraryProjectDirectory.isDirectory()) {
                final String libraryProjectPackageName = findPackageName(new File(
                        libraryProjectDirectory, "AndroidManifest.xml"));
                final File libraryProjectRJavaFile = findRJavaFile(new File(
                        libraryProjectDirectory, "gen"), libraryProjectPackageName);

                if (libraryProjectRJavaFile != null) {
                    try {
                        resources.addAll(getResourceList(libraryProjectRJavaFile));
                    } catch (final IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        return resources;
    }

    private static Set<ResSet> getResourceList(final File rJavaFile) throws IOException {
        final InputStream inputStream = new FileInputStream(rJavaFile);
        final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

        boolean done = false;

        final Set<ResSet> resources = new HashSet<ResSet>();

        String type = "";

        while (!done) {
            final String line = reader.readLine();
            done = (line == null);

            if (line != null) {
                final Matcher typeMatcher = sResourceTypePattern.matcher(line);
                final Matcher nameMatcher = sResourceNamePattern.matcher(line);

                if (nameMatcher.find()) {
                    resources.add(new ResSet(type, nameMatcher.group(3)));
                } else if (typeMatcher.find()) {
                    type = typeMatcher.group(1);
                }
            }
        }

        reader.close();
        inputStream.close();

        return resources;
    }

    private void searchFiles(final File parent, final File file, final FileType fileType) {
        if (isCanceled) {
            return;
        }
        if (file.isDirectory()) {
            for (final File child : file.listFiles()) {
                if (isCanceled) {
                    return;
                }
                searchFiles(file, child, fileType);
            }
        } else if (file.getName().endsWith(fileType.getExtension())) {
            try {
                if (isCanceled) {
                    return;
                }
                searchFile(parent, file, fileType);
                if (mCallback != null) {
                    mCallback.setProgress(file);
                }
            } catch (final IOException e) {
                System.err.println("There was a problem reading " + file.getAbsolutePath());
                e.printStackTrace();
            }
        }
    }

    private void searchFile(final File parent, final File file, final FileType fileType)
            throws IOException {
        final Set<ResSet> foundResources = new HashSet<ResSet>();

        final String fileContents = FileUtils.getFileContents(file);

        for (final ResSet resource : mResources) {
            if (isCanceled) {
                return;
            }
            final Matcher matcher = fileType.getPattern(resource.getType(),
                    resource.getName().replace("_", "[_\\.]")).matcher(fileContents);

            if (matcher.find()) {
                foundResources.add(resource);
            } else {
                final ResType type = sResourceTypes.get(resource.getType());

                if (type != null
                        && type.doesFileUseResource(parent, file.getName(), fileContents, resource
                                .getName().replace("_", "[_\\.]"))) {
                    foundResources.add(resource);
                }
            }
        }

        for (final ResSet resource : foundResources) {
            if (isCanceled) {
                return;
            }
            mUsedResources.add(resource);
            mResources.remove(resource);
        }
    }

    private void findDeclaredPaths(final File parent, final File file,
            final Map<String, ResType> resourceTypes,
            final Map<String, SortedMap<String, ResSet>> resources) {
        if (file.isDirectory()) {
            for (final File child : file.listFiles()) {
                if (isCanceled) {
                    return;
                }
                if (!child.isHidden()) {
                    findDeclaredPaths(file, child, resourceTypes, resources);
                }
            }
        } else {
            if (!file.isHidden()) {
                final String fileName = file.getName();

                String fileContents = "";
                try {
                    fileContents = FileUtils.getFileContents(file);
                } catch (final IOException e) {
                    e.printStackTrace();
                }

                for (final ResType resourceType : resourceTypes.values()) {
                    if (isCanceled) {
                        return;
                    }
                    final Map<String, ResSet> typeMap = resources.get(resourceType.getType());

                    if (typeMap != null) {
                        for (final ResSet resource : typeMap.values()) {
                            if (isCanceled) {
                                return;
                            }
                            if (resourceType.doesFileDeclareResource(parent, fileName,
                                    fileContents, resource.getName().replace("_", "[_\\.]"))) {
                                resource.addDeclaredPath(file.getAbsolutePath());

                                final String configuration = parent.getName();
                                resource.addConfiguration(configuration);
                            }
                        }
                    }
                }
            }
        }
    }
}


4.感兴趣的朋友可以联系我,加入到维护和后续版本的开发中去。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值