背景
现在正规的商业APP,大部分都会禁止在代码里直接用系统的Log直接打日志,原因大家都懂就不细说了。所以自定义一个Lint检测很有必要,如下图:
诸如此类的还有postDelayed
、new Thread
、fragment构造函数
、单类的代码行数
等等一些列常规方法和类检测。
随便举两个例子,来说明现实场景中可能存在的问题:
- 比如Fragment必须有空参构造函数,因为在内存不足的时候Fragment恢复默认是通过反射调用空参构造函数重建Fragment
- 比如直接使用了kt的扩展函数String.toInt,当服务端返回string不符合int的时候会发生NumberFormatException异常
这类问题在测试环境很难测出,review阶段也可能没注意到,直到灰度甚至全量在线上出现crash才被发现。
而Lint就是这么一种可以较低侵入、损失提前的解决方案。
当然除此之外,作为TL或者更高层面的角色,是希望大家能按照某种规则,把相关代码都写在统一要求的位置,那其实Lint也是不难做到的,如下图:
知识点储备
Lint是什么
In addition to ensuring your app meets its functional requirements by building tests, it’s important that you also ensure your code has no structural problems by running the code through lint. The lint tool helps find poorly structured code that can impact the reliability and efficiency of your Android apps and make your code harder to maintain.
For example, if your XML resource files contain unused namespaces, this takes up space and incurs unnecessary processing. Other structural issues, such as use of deprecated elements or API calls that are not supported by the target API versions, might lead to code failing to run correctly. Lint can help you clean up these issues.
官网说的废话比较多,相信读者看完背景
里提到的的例子已经大概能了解Lint能做什么,那我对Lint的理解是在代码生产过程中(编码、编译、git commit、CI阶段),用于提示开发者代码可能存在的问题,以及如何修复的这么一种轻量检测工具。
Lint的优势
- 功能强大,支持Java和Kt源文件、class文件、资源文件、Gradle等文件的检查
- 扩展性强,支持开发自定义Lint规则
- 有Google官方的支持,会和Android开发工具一起升级完善
- 在代码生产过程中全链路支持(编码、编译、commit、Gitlab CI)
- 支持文件导出html、xml,以及定义从某一个版本开始检测
自定义Lint开发流程
正题
Lint API
Issue
代表您想要发现并提示给开发者的一种问题,包含描述、更全面的解释、类型和优先级等等。官方提供了一个 Issue 类,我们只需要实例化一个 Issue,并注册到 IssueRegistry里
IssueRegistry
Lint规则加载的入口,提供要检查的Issue列表,详见下面的代码
Detector
负责扫描代码并找到有问题的地方,然后把它们报告出来。一个 Detector 可以报告多种类型的 Issue,可以针对不同类型的问题使用不同的严重程度
Scanner
用于扫描并发现代码中的Issue,每个Detector可以实现一到多个Scanner,具体如下表
需要实现的Scanner | 类描述 |
---|---|
UastScanner/SourceCodeScanner | 扫描 Java 文件和 Kotlin 文件 |
ClassScanner | 扫描 Class 文件 |
XmlScanner | 扫描 XML 文件 |
ResourceFolderScanner | 扫描资源文件夹 |
BinaryResourceScanner | 扫描二进制资源文件 |
OtherFileScanner | 扫描其他文件 |
GradleScanner | 扫描 Gradle 脚本 |
举个最简单的🌰
class DuLogDetector : Detector(), SourceCodeScanner {
companion object Issues {
private val IMPLEMENTATION = Implementation(
DuLogDetector::class.java, Scope.JAVA_FILE_SCOPE
)
/** Log call missing surrounding if */
@JvmField
val ISSUE = Issue.create(
id = "DuLogCheck",
briefDescription =