您的位置:首页 > 移动开发 > Android开发

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

2012-11-30 11:39 471 查看
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.感兴趣的朋友可以联系我,加入到维护和后续版本的开发中去。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: