静态测试技术之 Lint 冗余资源清理

引言

谈到冗余资源清理,我们不妨先来看看Android的资源组织方式和访问方式。

一、Android资源组织方式及访问方式

新建一个android工程后,默认资源路径res下生成对应的layout、drawable、values等子目录,分别对应以下几类常见的非代码资源:

layout,menu,anim等,代表res资源的顶层使用者,通过xml的方式组合控件,渐变动画等资源,给Activity等组件提供视图,通过这些xml脚本取代代码实现的布局&动效,解耦视图和界面逻辑,提升开发效率;

values,color,xml等,这一类代表res中的文本资源,都是xml格式资源,values主要存放arrays,attrs,colors,dimens,ids,string,integers,theme等基础资源,支持比较丰富的语言扩展;color主要存放返回color资源的selector资源;没有明确归属目录的xml资源,都可以放到xml目录下;

drawable,drawabld-xxx,raw等,这一类代表res中的多媒体资源,有比较丰富分辨率扩展,其中drawable目录主要存放返回drawable格式的selector,带nodpi标签主要存放一些与分辨率无关的9.png资源,其他带xxdpi等目录对应相应的分辨率机型,没有明确归属的非xml资源都可以放到raw目录下。

在资源目录中分类组织资源后,我们就可以通过引用资源 ID 来引用资源,所有资源的ID 都在项目中aapt工具自动生成的./gen/R类中定义,该文件不能被手动修改,当资源发生变动时,它会相应更新。

访问资源的方法主要有两种:

  • 在代码中:R.resource_type.resource_name (引用自定义资源) 或者android.R.resource_type.resource_name(引用系统标准资源),比如R.string.hello,string 是资源类型,hello 是资源名称,API可以通过这种语法来访问定义的资源,如:getResources().getString(R.string.hello);
  • 在 XML 中:使用对应的XML 语法,@[package:]type/name,例如@string/hello,string 是资源类型,hello 是资源名称,可以在 XML 资源通过该语法来访问定义的资源,如:android:text="@string/hello"

二、冗余资源的清理

随着长时间的版本迭代,工程中会冗余许多资源文件,手动查找删除效率太低难免有漏网之鱼,代码扫描工具可以方便的查找出未被引用的图片、ID等资源,本文主要应用Android lint的unUsedResources规则进行冗余资源查找清理。

Android Lint是针对Android的静态代码分析工具,能够对Android项目中潜在的bug、可优化的代码、安全性、性能、可用性、可访问性、国际化等进行检查。

在Android SDKTools 16及更高的版本中,Lint工具会自动安装。通过对Android工程源代码等进行扫描检查,可发现潜在的问题,更好的提升代码质量。

通过lint进行冗余资源清理主要有以下几种方式:

1、我们可以通过lint –check unUsedResources查找冗余资源列表然后手工或者通过其他删除工具加以清理。

2、如果工程使用的是gradle打包,可以在build.gradle中打开shrinkResources开关,这样打包的时候不会把冗余资源打包进来:shrinkResources true //移除无用的resource文件

3、也可以在Android Studio中使用Analyze-unUsedResources项查找出所有未被引用的资源列表:

在结果上右键选择ApplyFix’Android Lint Quick Fixes’,可以直接删除所有无用的资源:

可能存在的误删除与白名单配置 lint扫描工具无法判断出通过反射方式(android.content.res.Resources#getIdentifier)来获取的资源,可能会产生误删除,如:

此时资源被清理后界面上会找不到图片,如果工程中有该用法可以通过以下几种方式对资源添加白名单配置:

1、局部配置:在XML文件中通过tools:ignore="UnusedResources"属性配置忽略:

在xml文件开头声明命名空间tools,并为对应的element加上tools:ignore="UnusedResources "tools:ignore属性对其xml节点的所有子节点都生效:

<string name="string"tools:ignore="UnusedResources">我知道了</string>

2、 全局配置:在Android工程的根目录下创建一个名叫lint.xml的文件,如非xml资源可以通过这种方式添加白名单,IDE会读取根目录下的配置,命令行下可以通过—config指定具体配置,需要注意的是,如果工程根目录下存在lint.xml时,--config命令指定的参数无效:

配置文件中支持几个维度的自定义配置:

(1)规则id级别调整,置为ignore则该规则不生效,如:

(2)路径忽略,如:

(3)正则表达式忽略,如:

三、冗余资源清理原理解析

Lint扫描工具是如何扫描出冗余资源的呢,我们先来认识下LintUnusedResources扫描规则,从源码中规则的定义可以看到,UnusedResourceDetector继承自ResourceXmlDetector和Detector.JavaScanner ,查找范围包括Manifest,资源文件,java源文件及测试代码:

1、根据R.java获取资源列表:

Detetor类中JavaScanner接口定义的getApplicableNodeTypes()需要与createJavaVisitor()配合使用,getApplicableNodeTypes()返回我们感兴趣的Node列表,然后在createJavaVisitor()返回的AstVisitor中去处理这些Node。

定义一个AstVisitor的子类,并在createJavaVisitor()中返回它的一个实例,那么当扫描到符合定义语句对应的node就会触发UnusedResourceVisitor()中对应的回调函数:

2、查找代码中的引用:

Detetor类中JavaScanner接口定义的appliesToResourceRefsh()需要与visitResourceReference()函数配合使用,appliesToResourceRefsh()返回true,那么代码中的资源引用会触发visitResourceReference()处理函数:

3、同样的,查找xml文件中的引用:

4、从收集到的资源声明列表中删除被引用的资源列表并去除xml中声明不做处理(如tools:ignore="UnusedResources")或配置了白名单的资源,剩余的资源列表可认为是冗余资源:

5、report最终未被引用的资源列表:

四、手管的冗余资源清理应用

清楚了lint冗余资源的清理规则,我们可以放(小)心(心)地开始删删删了,谨慎起见,提供本地工具由开发童鞋本地清理确认,同时在持续集成平台自动监控冗余资源清理情况,形成一键清理+自动监控的灵活处理模式:

1、一键清理:在lint扫描结果的基础上提供命令行清理/还原工具,支持本地一键清理:

  • 清理: 调用lint unUsedResources扫描规则生成冗余资源的xml文件,解析该结果xml文件区分文件格式和xml属性格式的资源(资源格式见本文第一节),批量删除两种不同格式的资源,并在执行路径下生成备份路径按res原路径结构备份删除的内容,支持多次循环调用直至冗余资源结果为0。
  • 还原:将备份路径下的文件或xml属性资源还原到原路径,并自动添加到lint白名单。

2、自动监控:在持续集成平台上集成清理工具,输出冗余资源清理前后两个安装包及清理资源集,及时监控项目中的冗余资源情况,也可以直观看到清理带来的优化效果,推动项目组在发布前清理冗余资源。

也附上在手管6.8.1版本代码的清理结果:删除文件资源950+个,其他各类型属性2500+个,包大小缩小2.11M,预计在7.0页面改版可以达到更好的应用效果。

冗余资源清理是借助静态代码分析工具的一个小应用,大家在项目过程中是否有其他静态分析工具应用的场景呢? 欢迎大家一起探讨。

参考资源:

[1] https://developer.android.com/guide/topics/resources/index.html;

[2] Android资源管理-drawable篇;

[3] http://tools.android.com/tips/lint/writing-a-lint-check;

[4] https://android.googlesource.com/platform/tools/base/+/master/lint;

[5] 微桌面Android资源清理工具。

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏LanceToBigData

struts2(一)之初识struts2

前言   我们都知道struts2是一个框架,那什么是框架呢?很多人其实不太明白,其实框架就是一个半成品,别人将一些功能已经写好了,我们只需要拿来用即可,像我们...

1939
来自专栏互扯程序

程序猿必备调试工具postman

现在是资源共享的时代,同样也是知识分享的时代,如果你觉得本文能学到知识,请把知识与别人分享。

712
来自专栏北京马哥教育

linux系统性能监控与优化(3)–memory

虚拟内存以页的方式管理,一般为4K,如果配置了大页,则为2M The Page Frame Reclaim Algorithm.(PFRA) ...

3427
来自专栏前端架构与工程

webpack多页面开发与懒加载hash解决方案

本文内容只适用于webpack v1版本,webpack v2已经修复了hash计算规则。 之前讨论了webpack的hash与chunkhash的区别以及各...

1928
来自专栏散尽浮华

Keepalived使用梳理

keepalived介绍 keepalived观察其名可知,保持存活,在网络里面就是保持在线了,也就是所谓的高可用或热备,它集群管理中保证集群高可用的一个服务软...

4256
来自专栏Jack-Cui

Linux内核定时器timer_list

Linux内核版本:linux-3.0.35 开发板:i.MX6S MY-IMX6-EK200 拟定任务:LED闪烁 声明:嵌入式新手,如有错误还...

2040
来自专栏三丰SanFeng

redis主从集群搭建及容灾部署(哨兵sentinel)

Redis也用了一段时间了,记录一下相关集群搭建及配置详解,方便后续使用查阅。 提纲 l Redis安装 l 整体架构 l Redis主从结构搭建 l Redi...

3445
来自专栏跟着阿笨一起玩NET

原创C# 各种通用类集合 终于出炉了,觉得有用尽管拿去吧

已经开源,欢迎 Fork    https://github.com/chrisyanghua/MyHelper.git

412
来自专栏用户2442861的专栏

Redis 起步

http://www.cnblogs.com/shanyou/archive/2012/01/28/2330451.html

652
来自专栏技术博文

关于微信二次分享,描述变链接的解决方法(一)----文档说明

声明: 本篇博文只是个人工作中的分享总结,仅代表个人观点,虽然解决了不少网友的问题,但同时也引来了一些网友的不满,所以特此声明,当您遇到本博文解决不了的问题,可...

5167

扫码关注云+社区