导读

lint 是著名的 C/C++ 语言静态代码分析工具之一,Android Lint 顾名思义,针对 Android 的静态代码分析工具,能够对 Android 项目中潜在的 bug、可优化的代码、安全性、性能、可用性、可访问性、国际化等进行检查。

在 Android SDK Tools 16 及更高的版本中,Lint 工具会自动安装。通过对 Android 工程源代码等进行扫描检查,可发现潜在的问题,更好的提升代码质量。Android Lint 提供了命令行方式执行,也与 IDE(如 Eclipse、Android Studio)集成提供了 IDE 图形界面,单独输出的 xml 和 html 结果报告可以提供更丰富的信息。

lint 扫描规则初览

  1. NewApi 规则说明

初步扫描手管代码得到一份 html 的报告,结果分类比较清晰,但有 2000+error,12000+warning,,吓的手一抖直接关掉了。。

为了降低大家的心理门槛,我们从手管已经接入 Daily 的 NewApi 规则来看看 lint 是怎么工作的

最简单的,先来看看 NewApi 规则说明:

可以对 NewApi 规则有个大致的了解,扫描 App 中的 Android Api,对起始版本大于 AndroidManifest.xml 中声明的 minSdkVersion,即未加判断调用的高版本 Api 进行提示。没有判断调用不支持的 Api 会怎么样呢?低版本机器在执行到该代码段时就会抛出 NoSuchMethodException 异常 crash。

  1. NewApi 扫描结果

再来看看 NewApi 的扫描结果:

可以看到检查结果中的 issueid、summary、priority、severity、category、explanation 和说明中是一致的,message 中有更详细的代码段接口版本说明,location 字段中给出了对应的代码位置。

Issueid:规则名,唯一;

Summary:规则的简单概述;

Priority:优先级,1~10,10 为最严重

Severity:严重性,Fatal,Error,Warning,Information,Ignore

Category:类别,Correctness 正确性 Security 安全性 Performance 性能 Usability 可用性 Accessibility 可达性 i18n 国际化

Explanation:规则详细描述及问题解决建议

lint 规则实现原理篇

1.lint 规则主要模块

从 NewApi 在手管的落地实践来看 NewApi 确实是解决 Api 版本兼容性的一大利器,lint 是怎么实现这一规则的呢?lint 支持的 280+ 规则都是怎么实现的呢?

我们来看看 lint 规则的主要模块:

Issue:lint 规则定义,比如 NewApi,lint 已有规则列表维护在 BuiltinIssueRegistry 类中,目前 lint 官网提供有 280+ 个规则,可以按需打开也可以修改各个规则的严重级别,已有规则配置可以见实践篇;

Detetor:检索项目中检测项对应的问题,一个检测器可以检索多个独立但相关的问题,比如通过一个检测器查找多种 Manifest 相关的问题;

Implematation:连接检查项和检测器,也声明规则的查找范围,常用的 scope 包括 CLASS_FILE,JAVA_FILE,RESOURCE_FILE 等;

Registry:注册模块,lint 维护了一张所有规则的列表,检查规则通过注册添加到规则列表中;

####2.NewApi 规则注册类

从 NewApi 检查项的注册定义可以看到,issueid、summary 等均在 issue 注册时传入以便在结果报告中展示,Implematations 中 scope 声明了规则查找范围,Scope.CLASS_FILE 标明了 NewApi 检查项针对编译后的 class 字节码进行扫描:

3.NewApi 扫描核心类

再来看看 NewApi 扫描核心的 ApiDetector:

ApiDetector 检测器继承自 ResourceXmlDetector 并实现 Detector.ClassScanner 和 Detector.JavaScanner 接口,Detector 类中提供了 7 种 XXXScanner 接口

Scanner 也并不是直接进行代码行查找,scanner 中通过 lombok.ast(Abstract Syntax Tree 抽象语法树)API 来进行代码节点的查找,有兴趣的童鞋可以参照 Eclipse AST 介绍。

扫描规则实际上就是实现 detector 的过程,每个 detector 可以定义 1 个或多个不同类型的 issue,像 ApiDetector 中会处理多个 Api 调用相关的规则:NewApi,InlinedApi,Override,UnusedAttribute;

继续查看 ApiDetector 最主要的 checkClass() 可以更深入了解 NewApi 的扫描过程:

Api 版本库中维护了一份 Android 每个版本 Class 的类关系和成员变量,是 Api 兼容性检测的前提条件

首先进行类扫描处理,如果没有 TargetApi 定义的局部 miniSDK 则获取 AndroidManifest.xml 中 minSdkVersion 定义,首先进行继承类和接口类的扫描判断,发现的问题通过 report() 函数输出:

扫描结果 - 类兼容问题

然后开始对类节点的扫描处理,同样判断方法前是否有 TargetApi 标注定义了局部 miniSdk,依次检查类中 method、field、LDC 引用值,源码中可以看到在 method、field 的调用判断中,也对 android 常用的版本判断格式 if(Build.VERSION.SDK_INT >= XX) 的分支进行判断检查

对应的扫描结果中 message 字段返回了兼容性调用问题的类型及起始版本,并将发现的问题通过 report() 函数输出。

扫描结果-Field 调用兼容问题:

扫描结果-method 调用兼容问题:

自定义扫描规则篇

通过走读 lintNewApi 的实现过程,我们也清楚了 lint 中的规则是如何定义并实现的,我们自己是否也可以参照这个结构来自定义规则呢?答案是肯定的,lint 也支持自定义规则扩展,自定义规则通过 IssueRegistry 加入到规则表中和其他规则一起使用。什么场景适合自定义规则呢?比如手管 UI 库的编写规范,典型问题的修复情况,某些封装了不建议直接使用的 Api 的调用等都可以通过自定义规则来规范和提醒。

自定义 lint 规则是以 jar 形式存在的,通过继承 lint 的两个类来实现规则扩展:

①继承 IssueRegistry:自定义 Lint 规则的主类,有且只有一个,注册这个自定义 Lint 项目中有哪些自定义的 issue:

②继承 Detector 并实现 Detector 中合适的 XXXScanner 接口:可以根据需求实现多个自定义 Detector 类,在每个 Detector 类中实现自定义的一个或多个 issue;

在 eclipse 中新建 java 工程并引用 sdk\tools\lib\lint-api.jar 包,手动添加导出配置 MANIFEST.MF 文件

export 导出 jar 包,生成的 jar 包放到~/.android/lint/路径下,此时调用命令行工具就可以看到我们自定义的规则了

总结篇

管中窥豹,走读已有规则的实现可以让我们对工具有更全面的了解,更好的应用到项目中,网上关于自定义规则的示例也不多,源码中的规则实现也是一个很好的参照途径,也需要我们更进一步分析代码问题挖掘个中需求,才能发挥工具的更大作用。

本文简单结合手机管家 NewApi 的实践来了解 Lint 代码扫描过程,期待大家一起来探讨代码扫描工具有哪些更有价值的应用场景呢?

参考资料:

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

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

[3]https://www.bignerdranch.com/blog/building-custom-lint-checks-in-android/

[4] Android Lint: 静态检查 Android 版本兼容性问题 [5] Android Lint 工作原理剖析

【TMQ 新书专栏】https://weidian.com/?userid=984448577

原文链接:http://tmq.qq.com/2017/02/newapi_lint/

关注我们的微信公众号查看完整内容哦~~~~

想知道更多测试相关干货
请关注我们的微信公众号:腾讯移动品质中心 TMQ
二维码:
这里写图片描述

版权声明:腾讯 TMQ 拥有内容的全部版权,任何人或单位对本贴内容进行复制、转载时请申明原创腾讯 tmq,否则将追究法律责任。


↙↙↙阅读原文可查看相关链接,并与作者交流