如果对Lint以及如何自定义Lint规则的不了解的可以看一下我之前的一篇文章
自定义lint一篇文章就够了
想要打造通用可配置的自定义Lint规则首先我们要搞清楚两点
- 哪些内容可配置
- 什么代码可通用
哪些内容可配置
可配置主要分以下两点
- 要检测的内容(匹配规则,例如
java.lang.Thread
) - 提示纠错内容(命中规则后提示,例如:
请勿直接调用new Thread(),建议使用RXJava或统一的线程管理工具类
)
什么代码可通用
- 类
- 方法
- 属性字段
而lint检测匹配的内容,是根据被检测者路径来做匹配的
比如Log我们要检测的java.lang.Thread
或者com.google.gson.Gson
按照这个思路,我我可以先做一个匹配通过创建对象的一个规则
比如我们代码中,有很多可能要被替换的方法,通用的可以做一个map
class ConstructorDetector: Detector(), Detector.UastScanner {
private var constructorMap= mapOf(
"java.lang.Thread" to "请用线程池创建线程,避免直接new Thread()",
"com.google.gson.Gson" to "使用GsonUtil避免重复创建Gson对象",
)
companion object {
@JvmField
val ISSUE: Issue = Issue.create(
"NotNewUselessObject",
"避免直接创建不必要的对象",
"避免直接创建不必要的对象",
Category.CORRECTNESS,
7,
Severity.WARNING,
Implementation(ConstructorDetector::class.java, Scope.JAVA_FILE_SCOPE)
)
}
override fun getApplicableConstructorTypes(): List<String>? {
return constructorMap.keys.toList()
}
override fun visitConstructor(context: JavaContext, node: UCallExpression, constructor: PsiMethod) {
context.report(ISSUE, node, context.getLocation(node), getMessage(getConstructorClass(constructor)))
}
private fun getMessage(className: String): String {
return constructorMap[className]?:"缺少提示"
}
private fun getConstructorClass(constructor: PsiMethod):String {
var className=UastLintUtils.getQualifiedName(constructor)?:""
return if (className.isNullOrEmpty()) {
""
}else{
className.substring(0,className.lastIndexOf(".${constructor.name}"))
}
}
}
当然你可以把map配置成一个json文件,通过解析json来匹配lint规则。
我们通过配置了一个通用的创建建对象通用规则,而通用方法
与通用属性
思路大致相同
通过注解实现通用规则配置
还有一种思路是放在代码里面我们不关注 我们能不能做一个不关注对象,方法的,属性,我们只关注调用的一种通用匹配规则?
答案是有的
我们借鉴了注解@Deprecated
实现了注解@ReplaceWith
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.TYPEALIAS)
@Retention(AnnotationRetention.SOURCE)
annotation class ReplaceWith (val message: String)
messages是我们要提示的内容
我们只有需要关心有调用的地方具有@ReplaceWith
就已经命中了我们的规则,提出去message内容做为提示语
语言限定注解
我们的项目的Java和kotlin混编的,有一些用kotlin语法糖编写的方法可能只适用于在kotlin调用,不适用与Java
而我们@ReplaceWith
目前无法区分语言,为了解决这个问题。我们又定义了两种标识过滤注解
- @ScopeJava 表示只适用于Java代码
- @ScopeKotlin 表示只适用于kotlin代码
用法示例
@ReplaceWith(message = "建议使用 asViewClick,asViewShown,")
@ScopeKotlin
public BaseRecord setOt(String aOt) {
params.put(Config.KEY_OT, aOt);
return this;
}
这个例子想表达的是在Java中的setOt方法我们不做修改,但如果在kotlin使用该方法我们建议他使用@ReplaceWith中message的提示的两个方法asViewClick,asViewShown来替换
有人会问为什么我们有了通用配置方式,还要实现这种注解的方式,其实两者不冲突
当然这种使用注解的形势只适用我们工程里的代码,因为我们无法在Thread
和Gson
里增加这个注解,所以目前我们是一起使用的
写到最后
其实 自定义lint规则 特别是注解是如何实现,我并没有把具体的实现代码贴出来,而复杂的匹配规则也比自定义通用对象的要麻烦许多,而在探索和使用lint的时候我也的确遇到了很多坑
因为没有api国内也没有相应的文档,就算有也是很几年前放到现在是不行的。所以我更多是查看lint自带的规则源码,不断的尝试,过程的确比较痛苦,但是收货也很丰富
网友评论