美文网首页
如何打造通用可配置的自定义Lint规则

如何打造通用可配置的自定义Lint规则

作者: 不思进取的码农 | 来源:发表于2020-10-28 18:34 被阅读0次

    如果对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来替换

    有人会问为什么我们有了通用配置方式,还要实现这种注解的方式,其实两者不冲突
    当然这种使用注解的形势只适用我们工程里的代码,因为我们无法在ThreadGson里增加这个注解,所以目前我们是一起使用的

    写到最后

    其实 自定义lint规则 特别是注解是如何实现,我并没有把具体的实现代码贴出来,而复杂的匹配规则也比自定义通用对象的要麻烦许多,而在探索和使用lint的时候我也的确遇到了很多坑
    因为没有api国内也没有相应的文档,就算有也是很几年前放到现在是不行的。所以我更多是查看lint自带的规则源码,不断的尝试,过程的确比较痛苦,但是收货也很丰富

    (每天学习一点点.每天进步一点点,分享不宜路过点个赞呀,喜欢的点个关注后续更新不断)

    相关文章

      网友评论

          本文标题:如何打造通用可配置的自定义Lint规则

          本文链接:https://www.haomeiwen.com/subject/aixcvktx.html