Lint
Android Lint 是Android Studio 在ADT(Android Developer Tools)16提供的代码扫描工具,可以帮助我们发现和更正代码结构质量的问题。系统会报告该工具检测到的每个问题并提供问题的描述消息和严重级别,以便快速确定需要优先进行的修改。此外,我们还可以通过降低问题的严重级别以忽略与项目无关的问题,或者提高严重级别以突出特定问题。
- 无需实际执行应用
- 不必编写测试用例
下图显示了 lint 工具如何处理应用源文件。
右侧窗格显示选定错误类别、类型或问题的检查报告,并提供错误的名称和位置。在适用情况下,检查报告会显示问题概要等其他信息,以帮助您更正问题。
allowBackup属性确定是否可以备份应用程序的数据并恢复。
在SDK版本23及更高版本中,您的应用程序数据将在应用程序安装时自动备份和还原。考虑添加属性“ android:fullBackupContent”以指定“ @xml”资源,该资源配置要备份的文件。这个问题属于Security。
- 在Gradle命令行环境下,可直接用执行Lint检查。
想使用Lint命令需要配置Lint环境变量,或者进入%ANDROID_HOME% oolsbin目录下
会在命令行中输出相应信息:
在一个项目目录下运行lint检查一系列文件,使用下面的命令:
例如,你可以执行下面的命令,来扫描项目目录下和它的子目录下的文件。
MissingPrefix问题ID告诉lint只扫描缺少Android命名空间前缀的XML属性
- 检查项目中是否存在某个问题
该功能允许我们输入一个Issue的id来检查项目中是否存在该id对应的Issue的问题
会在Inspection Results窗口显示对应的问题信息
- 遗漏的翻译(比如国际化未被翻译的字段)
- 布局性能(解决布局无用或太多,嵌套过多问题)
- 没有引用的资源文件
- 不一致的数组大小
- 国际化的硬编码问题
- 图标重复问题
- 可用性问题(文本输入类型没有指定)
- manifest文件内的错误
Lint 发现的每个问题都有描述信息和等级,我们可以很方便地定位问题,同时按照严重程度进行解决。当然这个“严重程度”我们可以手动调节,有些原则问题不容侵犯,必须提升到 error,而有的个别问题也可以无视。
- Correctness : 正确性,比如硬编码、使用过时 API 等
- Performanc : 性能 有影响的编码,比如:静态引用,循环引用等
- Internationalization : 国际化,直接使用汉字,没有使用资源引用等
- Security : 安全性,比如在 WebView 中允许使用 JavaScriptInterface 等
- Usability : 易用性,有更好的替换的 比如排版、图标格式建议.png格式 等
- Accessibility : 无障碍,比如ImageView的contentDescription往往建议在属性中定义 等
从高到低:
- Fatal : 致命的 , 该类型的错误会直接中断 ADT 导出 APK。
- Error : 错误,明确需要解决的问题,包括Crash、明确的Bug、严重性能问题、不符合代码规范等,必须修复。
- Warning : 警告,包括代码编写建议、可能存在的Bug、一些性能优化等,适当放松要求。
- Information
- Ignore
默认情况下,运行 lint 扫描时,lint 工具会检查它支持的所有问题。 但是我们可以限制 lint 要检查的问题,并为这些问题分配严重级别。例如,禁止对与项目无关的特定问题进行 lint 检查,也可以将 lint 配置为以较低的严重级别报告非关键问题。
- 全局(整个项目)
- 项目模块
- 生产模块
- 测试模块
- 打开的文件
- 类层次结构
- 版本控制系统 (VCS) 范围
在项目根目录创建一个lint.xml文件
lint.xml文件由一个封闭的父标签组成,它包含了一个或者多个子标签。Lint为每一个定义了唯一的id属性值,通过设置标识的安全属性,你可以改变一个问题的安全级别,或者这个问题的lint检查,并且可以指定该issue作用于指定的文件还是当前项目。把lint.xml放在app的目录下(需要放在build.gradle同级目录),命令行执行lint时候,lint就会用lint.xml中的规则。另外,执行lint时还可以用参数–config指定一个全局的配置用于所有的项目。当项目中已有lint.xml,则对于某个issue而言,在lint.xml中没有对该issue特别定制的情况下,–config指定的文件中的该issue的定制才起作用。
要专门对 Android 项目中的某个类或方法停用 lint 检查,可以向该代码添加 注释。
可以使用 属性对 XML 文件的特定部分停用 lint 检查。在 文件中添加以下命名空间值,以便 lint 工具能够识别该属性
对 XML 布局文件的 元素中的 问题关闭 lint 检查,
如果某个父元素声明了 属性,则该元素的子元素会继承此属性。在本例中,也会对 子元素停用 lint 检查。
此时不会提示SmallSp错误
禁止 lint 检查 XML 元素中的所有问题:
禁止检查多个问题:
before:
add this:
after:
比如这个SmallSp,一般情况下是一个黄色的警告,在gradle中配置成fatal之后,此处提示为红色错误,并且在gralde构建时,如果存在SmallSp问题会停止构建。
可以为项目的当前警告集创建快照,然后将该快照用作将来运行检查的基准,以便只报告新问题。 有了基准快照,您便可开始使用 lint 让构建失败,而不必先返回并解决所有现有问题。
基准 :当前问题集
创建Lint基准快照
修改项目的 文件
首次添加此代码行时,系统会创建 文件以建立基准。此后,lint 工具仅读取该文件以确定基准。
如果要创建新基准,请手动删除该文件并再次运行 lint 以重新创建它。
然后,从 IDE(依次选择 Analyze > Inspect Code)或从命令行运行 lint,如下所示。系统会输出 文件的位置。
运行 会将所有当前问题记录在 文件中。当前问题集称为“基准”,如果要与其他人共享 文件,可以将其加入版本控制。
创建基准后,如果向代码库添加任何新警告,lint 将仅列出新引入的错误。
如上图所示,执行上述操作后,会把所有问题都放入 中,在此之后出现的新问题,会单独展示出来,例如上图中的
Android Studio 附带了许多 lint 及其他检查配置文件,这些配置文件可通过 Android 更新进行更新。您可以原封不动地使用这些配置文件,也可以修改它们的名称、说明、严重级别和范围。您还可以激活和禁用整组的配置文件或一组配置文件中的个别配置文件。
要访问 Inspections 对话框,请执行以下操作:
-
依次选择 Analyze > Inspect Code。
-
在 Specify Scope 对话框的 Inspection Profile 下,点击 More(省略号)。
此时将显示 Inspections 对话框,其中列出了支持的检查及其说明。
-
选择 Profile 下拉列表,以在 Default (Android Studio) 与 Project Default(活动的项目)检查之间切换。如需了解详情,请参阅以下 IntelliJ 页面:“Specify Inspection Scope”对话框。
-
在左侧窗格的 Inspections 对话框中,选择一个顶级配置文件类别,或展开一个组并选择特定的配置文件。选择一个配置文件类别后,您可以将该类别中的所有检查项目当作一个检查项目进行修改。
-
选择 Manage 下拉列表,以复制检查项目、对检查项目进行重命名、向检查项目添加说明以及导出/导入检查项目。
-
操作完成后,点击 OK。
Android Lint内置了很多lint规则,用来检测一些常见的代码问题(例如,正确性问题、安全问题、性能问题,等等)。同时,Android Lint也支持自定义lint规则,以便开发者灵活应用,更好地提升项目代码质量 。利用自定义lint规则,既可以用来在项目中检测代码质量问题,也可以用来保证编码规范的执行。
Detector 是自定义规则的核心,它的作用是扫描代码,从而获取代码中的各种信息,然后基于这些信息进行提醒和报告。
以下是可以实现的扫描器接口,选择实现哪种接口取决于你想要的扫描范围。
- Detector.BinaryResourceScanner 针对二进制资源,例如 res/raw 等目录下的各种 Bitmap
- Detector.JavaScanner / JavaPsiScanner / UastScanner 针对 Java 代码进行扫描
- Detector.ClassScanner 相对于 Detector.JavaScanner,更针对于类进行扫描,可以获取类的各种信息
- Detector.GradleScanner 针对 Gradle 进行扫描
- Detector.ResourceFolderScanner 针对资源目录进行扫描,只会扫描目录本身
- Detector.XmlScanner 针对 xml 文件进行扫描
- Detector.OtherFileScanner 用于除上面6种情况外的其他文件
扫描Java源文件的Scanner先后经历了三个版本。
-
最开始使用的是JavaScanner,Lint通过Lombok库将Java源码解析成AST(抽象语法树),然后由JavaScanner扫描。
-
在Android Studio 2.2和lint-api 25.2.0版本中,Lint工具将Lombok AST替换为PSI,同时弃用JavaScanner,推荐使用JavaPsiScanner。
PSI是JetBrains在IDEA中解析Java源码生成语法树后提供的API。相比之前的Lombok AST,可以支持Java 1.8、类型解析等。使用JavaPsiScanner实现的自定义Lint规则,可以被加载到Android Studio 2.2+版本中,在编写Android代码时实时执行。
-
在Android Studio 3.0和lint-api 25.4.0版本中,Lint工具将PSI替换为UAST(通用抽象语法树),同时推荐使用新的UastScanner。
UAST是JetBrains在IDEA新版本中用于替换PSI的API。UAST更加语言无关,除了支持Java,还可以支持Kotlin。
PSI(Program Structure Interface)是IDEA中用于解析代码的一套API,全称是:程序结构接口 。可将文件的内容表示为特定编程语言中的元素的层级结构。
A PSI (Program Structure Interface) file is the root of a structure representing the contents of a file as a hierarchy of elements in a particular programming language.
每种Psi元素对应一个类,均继承自。例如PsiMethodCallExpression表示方法调用语句,PsiNewExpression表示对象实例化语句等。
官方文档
IntelliJ Platform SDK DevGuide www.jetbrains.org/intellij/sd…
UAST全称是通用抽象语法树,UAST节点本质上是Java和Kotlin所支持的超集。
使用UAST编写规则的时候,这个规则会同时适用Java文件和Kotlin文件,无需为同一个对象编写两套规则。
JavaPsiScanner中包含6组、12个回调方法,如下。
- 当返回了需要检查的Psi元素类型列表时,类型匹配的Psi元素()就会被返回的检查。
- 当返回方法名的列表时,名称匹配的方法调用()就会被检查。
- 当返回类名的列表时,类名匹配的构造语句()就会被检查。
- 当返回引用名的列表时,名称匹配的引用语句()就会被检查。
- 当返回true时,Java代码中的资源引用(例如)就会被检查。
- 当返回父类名的列表时,父类名匹配的类声明()就会被检查。
此处用第二种做了示例
关键代码:
MyIssueDetector
当发现有调用等方法的时候,我们都会收到回调, 因为我们只是看了方法名,而不知道类,所以需要使用鉴别器(eveluator)进行检查,
确保它是位于中的,如果是的话就上报一个用例。
id : 唯一值,应该能简短描述当前问题。利用Java注解或者XML属性进行屏蔽时,使用的就是这个id。
summary : 简短的总结,通常5-6个字符,描述问题而不是修复措施。
explanation : 完整的问题解释和修复建议。
category : 问题类别。
priority : 优先级。1-10 递增。
severity : 严重级别:Fatal, Error, Warning, Informational, Ignore。
Implementation : 为Issue和Detector提供映射关系,Detector就是当前Detector。声明扫描检测的范围
Scope:用来描述Detector需要分析时需要考虑的文件集,包括:Resource文件或目录、Java文件、Class文件。
返回值指定了需要被检查的方法
找到与[.getApplicableMethodNames]返回的任何名称匹配的任何方法调用而调用的方法。
context.report的参数:
第一个参数:是我们定义的Issue; 第二个参数:根据当前节点返回当前的位置信息,便于在报告中显示定位; 第三个参数:字符串用来为警告添加解释。
MyIssueRegistry
创建好的要在中注册
IssueRegistry 中注册了 Android Lint 自带的 Issue,而自定义的 Issue 则可以通过 getIssues 系列方法传入
build.gradle
添加lint相关依赖,并生成jar包
其中 lint-api 是 Android Lint 的官方接口,基于这些接口可以获取源代码信息,从而进行分析。
lint-checks 是官方已有的检查规则。
Lint-Registry 表示给自定义规则注册,以及打包为 jar。
将生成的jar包放入~android/lint文件夹中(如果没有就自己建一个) 我自己的 C:Userszhuoy.androidlint
之后使用命令行工具查看是否添加成功:
与此同时,lint --show/list 均可以查看到这条Issue
被测试的代码:
Google 官方的方案是把 jar 文件放到 ~/.android/lint/,如果本地没有 lint 目录可以自行创建,这个使用方式较为简单,但也使得 Android Lint 作用于本地所有的项目,不大灵活。 在主项目中新建一个 Module,将jar引入module,这样各个项目可以以 module 的方式自行引入自定义 Lint,比较灵活,项目之间不会造成干扰。
新建一个Android Library, 在build.gradle中添加以下代码:
这里,创建了一个叫“lintJarImport”的Gradle configuration,其引用了模块 “:lint_lib”的Gradle configuration “lintChecks”。
同时,对内置的Gradle task “compileLint”做了修改,让其依赖于我们定义的一个task “copyLintJar”。在task “copyLintJar”中,把模块 “:lint_lib”输出的jar包拷贝到了build/intermediates/lint/lint.jar。
然后执行 看结果
其他项目只要引入这个lintlibrary即可使用其中定义的Lint规则。
在代码中也可以看到高亮提示。
有时IDE给出提示的同时,会有一个ALT+ENTER的快捷键来让我们直接使用它的建议修改代码,方便快捷,我们也可以如此:
下图是我修改了一下检查的内容:发现使用的时候,给出提示,并且给一个建议使用来代替。
wtf不是what the fuck,而是 What a terrible failure
实现方式:
相较于之前的上报信息,我们在调用方法时,后面加了一个lintFix的参数。
LintFix是Lint的一个快速修复的方式。
independent :此修补程序是否独立于要应用的其他修补程序。