1. 为啥用Lint
平时开发中我们在提mr的时候都会进行review,但有些问题通过人眼去看很难发现,比如Fragment必须有空参构造函数,因为在内存不足的时候Fragment恢复默认是通过反射调用空参构造函数重建Fragment、又或者直接使用了kt的扩展函数String#toInt,当服务端返回string不符合int的时候会发生NumberFormatException异常,这类问题在测试环境很难测出,review阶段也可能没注意到,直到线上出现crash才被发现。
那为了避免这一类问题,我们最开始是将发生过的问题都记录在checklist中,在review的时候着重去看,但靠人眼去看难免会有遗漏。那为了彻底杜绝checklist中的问题不在发生,有没有一种方法能在review之前进行自动扫描,将checklist中的问题都检查一遍呢,答案是有的,也就是今天要提到的Lint。
Lint功能强大,有诸多的优势:
-
功能强大,Lint支持Java和Kt源文件、class文件、资源文件、Gradle等文件的检查。
-
扩展性强,支持开发自定义Lint规则。
-
配套工具完善,Android Studio、Android Gradle插件原生支持Lint工具。
-
Lint专为Android设计,原生提供了几百个实用的Android相关检查规则。
-
有Google官方的支持,会和Android开发工具一起升级完善。
为了避免各位提不起兴趣,这里先放几张效果图
可以在AS编码时实时提示
也可以将扫描结果以报告形式输出。
2. Lint Api简介
在开始前提一句,尽量不要用中文去搜Lint的资料,不然就会像我一样,3天demo才跑起来。因为网上搜的文章大部分都是几年前的,按照那样配置跑不起来。
2.1 Lint Api
言归正传先介绍下Lint相关类的作用
- Issue:用来声明一个Lint规则。
- Detector:用于检测并报告代码中的Issue,每个Issue都要指定Detector。
- Scanner:用于扫描并发现代码中的Issue,每个Detector可以实现一到多个Scanner。
- IssueRegistry:Lint规则加载的入口,提供要检查的Issue列表。
下面看个自定义规则,实现了Serializable接口的类,引用类型成员变量也必须要实现Serializable接口
class SerializableClassDetector : Detector(), Detector.UastScanner {
companion object {
private const val REPORT_MESSAGE = "该对象必须要实现Serializable接口,因为外部类实现了Serializable接口"
private const val CLASS_SERIALIZABLE = "java.io.Serializable"
val ISSUE = Issue.create(
"SerializableClassCheck",
REPORT_MESSAGE,
REPORT_MESSAGE,
Category.CORRECTNESS,
10,
Severity.ERROR,
Implementation(SerializableClassDetector::class.java, Scope.JAVA_FILE_SCOPE)
)
}
override fun applicableSuperClasses(): List<String>? {
return listOf(CLASS_SERIALIZABLE)
}
override fun visitClass(context: JavaContext, declaration: UClass) {
for (field in declaration.fields) {
//字段是引用类型,并且可以拿到该class
val psiClass = (field.type as? PsiClassType)?.resolve() ?: continue
if (!context.evaluator.implementsInterface(psiClass, CLASS_SERIALIZABLE, true)) {
context.report(ISSUE, context.getLocation(field.typeReference!!), REPORT_MESSAGE)
}
}
}
}
然后在IssueRegister中注册
class CustomIssueRegistry : IssueRegistry() {
override val issues: List<Issue>
get() = listOf(
SerializableClassDetector.ISSUE
)
override val api: Int
get() = CURRENT_API
}
2.2 Lint Debug
看起来是比较简单的,坑的是api一点文档没有,基本所有api都要靠猜和试,所以掌握一手debug非常重要,好在Lint提供了测试框架com.android.tools.lint:lint:$lintVersion
,这与java的单元测试非常像,我们只需继承LintDetectorTest
重写对应方法即可进行debug,看个例子
class SerializableDetectorTest : LintDetectorTest() {
override fun getDetector(): Detector {
return SerializableClassDetector()
}
override fun getIssues(): MutableList<Issue> {
return mutableListOf(SerializableClassDetector.ISSUE)
}
fun test() {
lint()
.files(
kotlin("""