利用 Python 脚本与 Lint 工具清理 Android 项目无用资源
本文介绍如何使用 Python 脚本结合 Android Lint 工具自动清理项目中的无用资源。通过解析 Lint 生成的 XML 报告,脚本能识别未使用的 Drawable、XML 布局等资源,并根据节点数量执行文件删除或节点移除操作,有效减小应用体积并优化构建速度。
原理与准备工作
在 Android 开发中,未使用的资源文件(如图片、布局 XML)会增加应用体积并拖慢构建速度。Android SDK 自带的 lint 工具可以扫描项目并生成详细的报告。为了自动化清理过程,我们可以编写一个 Python 脚本 delUnused.py,将其放置在项目根目录下直接运行。
该脚本的核心逻辑分为两步:首先调用 Lint 命令生成 XML 格式的检查结果,然后解析该 XML 文件,识别出三类无用资源(如 Drawable、Layout 等),并执行相应的删除或节点移除操作。
执行 Lint 命令
脚本首先通过 subprocess 模块执行 Lint 命令。我们需要指定检查项为 UnusedResources,输出格式为 --xml,并提供结果保存路径和项目路径。
lint --check UnusedResources --xml [resultPath] [projectPath]在 Python 中,这通过 exec_lint_command 函数实现。它启动子进程,读取标准输出并打印结果,确保命令执行无误。
def exec_lint_command():
cmd = 'lint --check UnusedResources --xml %s %s' % (_get_lint_result_path(), _get_project_dir_path())
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
c = p.stdout.readline().decode()
while c:
print(c)
c = p.stdout.readline().decode()解析 Lint 报告
Lint 生成的 XML 报告包含具体的问题描述。例如,一个典型的 issue 节点如下所示,其中 id 标识问题类型,message 描述具体资源,location 指向文件路径和行号。
<issue
id="UnusedResources"
severity="Warning"
message="The resource `R.layout.activity_all_player` appears to be unused"
category="Performance"
priority="3"
summary="Unused resources"
explanation="Unused resources make applications larger and slow down builds."
errorLine1="<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
"
errorLine2="^"
quickfix="studio">
<location
file="res\layout\activity_all_player.xml"
line="2"
column="1"/>
</issue>脚本使用 xml.dom.minidom 解析生成的 XML 文件。核心函数 _parse_lint_report 获取根节点,并调用 _parse_xml 遍历子节点。对于每个 issue 节点,脚本提取 id、severity 和 message 属性,并进一步解析 location 子节点以获取文件路径、行号和列号。
def _parse_lint_report():
file = minidom.parse(_get_lint_result_path())
root = file.documentElement
beans = _parse_xml(root)
return beansdef _parse_xml(element, beans=None):
if beans is None:
beans = []
for node in element.childNodes:
if node.nodeName == ISSUE_KEY and node.nodeType is node.ELEMENT_NODE:
lint_bean = _LintBean()
lint_bean.id = node.getAttribute(ID_KEY)
lint_bean.severity = node.getAttribute(SEVERITY_KEY)
lint_bean.message = node.getAttribute(MESSAGE_KEY)
_parse_location(node, lint_bean)
lint_bean.print()
beans.append(lint_bean)
return beans为了存储解析结果,脚本定义了 _Location 和 _LintBean 两个类。前者存储文件路径、行号和列号,后者封装完整的 lint 信息,并提供 print 方法用于调试输出。
class _Location(object):
def __init__(self):
self.file = ''
self.line = 0
self.column = 0
class _LintBean(object):
def __init__(self):
self.id = ''
self.severity = ''
self.message = ''
self.location = _Location()
def print(self):
print('find a %s, cause: %s. filePath: %s. line: %s' % (
self.id, self.message, self.location.file, self.location.line))执行清理操作
解析完成后,脚本遍历所有 lint 结果。只有当 id 为 UnusedResources 时才进行处理。根据 location.line 是否为空,脚本区分两种情况:
- 单节点资源:如果
line字段存在,说明该资源在 XML 中仅有一个引用节点。此时直接调用del_file删除整个资源文件。 - 多节点资源:如果
line字段为空,说明该资源在 XML 中可能有多个引用节点(例如一个布局文件中包含多个同名控件)。此时需要调用del_node删除特定的 XML 节点,而不是删除整个文件。
for lint in lint_result:
total_unused_resource += 1
if lint.id != 'UnusedResources':
continue
if lint.location.line != '':
is_single = _is_single_node(lint.location.file)
if is_single:
total_del_file += 1
del_file(lint.location.file)
else:
total_remove_attr += 1
node_name = get_node_name(lint.message)
del_node(lint.location.file, node_name)
else:
total_del_file += 1
del_file(lint.location.file)删除文件的操作通过 os.remove 实现,并捕获 FileNotFoundError 以处理文件已被手动删除的情况。删除 XML 节点时,脚本重新解析目标文件,遍历子节点,找到匹配 name 属性的节点后调用 removeChild,最后使用 writexml 将修改写回文件。
def del_file(file_path):
try:
os.remove(file_path)
print('remove %s success.' % file_path)
except FileNotFoundError:
print('remove %s error.' % file_path)def del_node(file_path, node_name):
file = minidom.parse(file_path)
root = file.documentElement
nodes = root.childNodes
for node in nodes:
if node.nodeType in (node.TEXT_NODE, node.COMMENT_NODE):
continue
if node_name == node.getAttribute('name'):
root.removeChild(node)
file.writexml(open(file_path, 'w', encoding='UTF-8'), encoding='UTF-8')
print('remove %s, node_name:%s. success!' % (file_path, node_name))
return注意事项与风险
在执行此类自动化清理脚本前,请务必注意以下风险:
- 备份项目:删除操作不可逆,建议在运行脚本前对项目目录进行完整备份或使用版本控制系统(如 Git)提交当前状态。
- 验证结果:Lint 报告可能存在误报,特别是对于动态加载的资源或反射引用的资源。建议在正式环境使用前,先在测试分支或小规模项目中验证脚本的准确性。
- 权限问题:确保运行脚本的用户对项目目录及资源文件具有读写权限。
以上就是利用 Python 和 Lint 删除项目无用资源的详细内容,更多关于 Android 开发优化、Python 自动化脚本的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









