Android代码检查规则Lint的自定义与应用详解

作者:聆听Tech 时间:2021-11-04 22:13:43 

前言:

在日常的代码开发中,此处相信每个开发人员对代码质量都是高要求,有自己的一套代码规范,但是我们不是单独作战,往往大家都是团队作战,人是最大的变量,各人各异,如何保证团队的代码质量和代码规范呢?靠开发者自觉吗?也许有的团队有严格的CR机制,在MR阶段会进行CR,CR不通过的MR是不允许合入的,但是这样会使Reviewer花费较多的时间去校验,那么这时候我们就需要在编码过程中提供一种代码检测机制。

例如:期望表现的效果就是在编码时可以展示异常检测的方法,高亮或者标红,当鼠标悬停在高亮的代码上时,会提供问题的描述和解决方法。需要这种效果,就需要自定义lint了。

Android代码检查规则Lint的自定义与应用详解

什么是Lint

在Android Studio中提供的代码扫描工具Lint,可在无需实际执行该应用,也不必编写测试用例的情况下帮助开发者发现代码质量问题和提出一些改进建议。

Lint工具可检查您的 Android 项目源文件是否包含潜在错误,以及在正确性、安全性、性能、易用性、便利性和国际化方面是否需要优化改进。在使用 Android Studio 时,配置的 Lint 和 IDE 检查会在您每次构建应用时运行。不过,您可以手动运行检查或从命令行运行 Lint。

android studio内置了较多的lint规则,但内置的lint规则无法满足直观的适合我们时,就需要我们自定义lint了。

Android代码检查规则Lint的自定义与应用详解

自定义Lint流程:

1. 新创建module,Module类型选择Java or Kotlin Library, 暂时命名lint_tools

2. 在build.gradle中引入lint的依赖

dependencies {
       compileOnly 'com.android.tools.lint:lint-api:27.2.2'
       compileOnly 'com.android.tools.lint:lint-checks:27.2.2'
   }

在moudle中依赖了lint-api和lint-checks,其中lint-api就是lint相关的api,lint-checks就是android studio里自定义的一些lint规则,我们自定义lint可以参考lint-checks里面的写法。

3. 本地创建个资源id命名检查规则,用来规范项目中的id统一命名

创建ViewIdDetector,直接继承LayoutDetector(也可以继承ResourceXmlDetector 或者 继承Detector实现的接口是XmlScanner,方式多样)

import com.android.SdkConstants
import com.android.tools.lint.detector.api.*
import com.android.tools.lint.detector.api.Category.Companion.CORRECTNESS
import com.android.tools.lint.detector.api.Scope.Companion.RESOURCE_FILE_SCOPE
import org.w3c.dom.Element

class ViewIdDetector : LayoutDetector() {

override fun getApplicableElements(): Collection<String>? {
       return listOf(
           SdkConstants.TEXT_VIEW,
           SdkConstants.IMAGE_VIEW,
           SdkConstants.BUTTON
       )
   }

   override fun visitElement(context: XmlContext, element: Element) {
       if (!element.hasAttributeNS(SdkConstants.ANDROID_URI, SdkConstants.ATTR_ID)) {
           return
       }
       val attr = element.getAttributeNodeNS(SdkConstants.ANDROID_URI, SdkConstants.ATTR_ID)
       val value = attr.value
       if (value.startsWith(SdkConstants.NEW_ID_PREFIX)) {
           val idValue = value.substring(SdkConstants.NEW_ID_PREFIX.length)
           var matchRule = true
           var expMsg = ""
           when (element.tagName) {
               SdkConstants.TEXT_VIEW -> {
                   expMsg = "tv"
                   matchRule = idValue.startsWith(expMsg)
               }
               SdkConstants.IMAGE_VIEW -> {
                   expMsg = "iv"
                   matchRule = idValue.startsWith(expMsg)
               }
               SdkConstants.BUTTON -> {
                   expMsg = "btn"
                   matchRule = idValue.startsWith(expMsg)
               }
           }
           if (!matchRule) {
               context.report(
                   ISSUE,
                   attr,
                   context.getLocation(attr),
                   "ViewIdName建议使用view的缩写_xxx; ${element.tagName} 建议使用 `${expMsg}_xxx`"
               )
           }
       }
   }

   companion object {
       val ISSUE: Issue = Issue.create(
           "ViewIdCheck",
           "ViewId命名不规范",
           "ViewIdName建议使用 view的缩写加上_xxx,例如tv_xxx, iv_xxx",
           CORRECTNESS,
           5, Severity.ERROR,
           Implementation(
               ViewIdDetector::class.java,
               RESOURCE_FILE_SCOPE
           )
       )
   }
}

自定义Detector可以实现一个或多个Scanner接口,选择实现哪种接口取决于你想要的扫描范围。
Lint API 中内置了很多 Scanner:

Scanner 类型Desc
UastScanner扫描 Java、Kotlin 源文件
XmlScanner扫描 XML 文件
ResourceFolderScanner扫描资源文件夹
ClassScanner扫描 Class 文件
BinaryResourceScanner扫描二进制资源文件
GradleScanner扫描Gradle脚本

4. 实现IssueRegistry并添加对应的自定义Issue:

class IMockIssueRegistry: IssueRegistry() {
   override val issues: List<Issue>
       get() = listOf(
           ViewIdDetector.ISSUE
       )

}

5. 在module(lint_tools)中对应的build.gradle中配置如下信息:

jar {
   manifest {
       attributes("Lint-registry-v2": "com.imock.lint.IMockIssueRegistry")
   }
}

6. 在需要进行lint检查的module中或者app目录现的build.gradle中引用对应的lint_tools即可使用。

dependencies {
   lintChecks project(path: ':lint-tools')
}

至此你可以试着自己自定义Lint了,相关语法api都可参考lint-checks中提供的Detector实现,来实现自己的Lint检查规则。

拓展一下:

1. 针对Issue.create参数了解一下:

companion object {
   /**
    * Creates a new issue. The description strings can use some simple markup;
    * see the [TextFormat.RAW] documentation
    * for details.
    *
    * @param id the fixed id of the issue
    * @param briefDescription short summary (typically 5-6 words or less), typically
    * describing the **problem** rather than the **fix**
    * (e.g. "Missing minSdkVersion")
    * @param explanation a full explanation of the issue, with suggestions for
    * how to fix it
    * @param category the associated category, if any
    * @param priority the priority, a number from 1 to 10 with 10 being most
    * important/severe
    * @param severity the default severity of the issue
    * @param implementation the default implementation for this issue
    * @return a new [Issue]
    */
   @JvmStatic
   fun create(
       id: String,
       briefDescription: String,
       explanation: String,
       category: Category,
       priority: Int,
       severity: Severity,
       implementation: Implementation
   ): Issue {
       val platforms = computePlatforms(null, implementation)
       return Issue(
           id, briefDescription, explanation, category, priority,
           severity, platforms, null, implementation
       )
   }
}
  • 参数id 唯一的id,简要表面当前提示的问题。

  • 参数briefDescription 简单描述当前问题

  • 参数explanation 详细解释当前问题和修复建议

  • 参数category 问题类别

  • 参数priority 优先级,从1到10,10最重要

  • 参数Severity 严重程度:FATAL(奔溃), ERROR(错误), WARNING(警告),INFORMATIONAL(信息性),IGNORE(可忽略)

  • 参数Implementation Issue和哪个Detector绑定,以及声明检查的范围。Scope有如下选择范围:RESOURCE_FILE(资源文件),BINARY_RESOURCE_FILE(二进制资源文件),RESOURCE_FOLDER(资源文件夹),ALL_RESOURCE_FILES(所有资源文件),JAVA_FILE(Java文件), ALL_JAVA_FILES(所有Java文件),CLASS_FILE(class文件), ALL_CLASS_FILES(所有class文件),MANIFEST(配置清单文件), PROGUARD_FILE(混淆文件),JAVA_LIBRARIES(Java库), GRADLE_FILE(Gradle文件),PROPERTY_FILE(属性文件),TEST_SOURCES(测试资源),OTHER(其他);

来源:https://juejin.cn/post/7084996427662917640

标签:Android,Lint
0
投稿

猜你喜欢

  • JavaWeb 网上书店 注册和登陆功能案例详解

    2022-09-20 02:53:22
  • 浅析Java中comparator接口与Comparable接口的区别

    2023-11-01 20:31:14
  • JavaWeb中Servlet的深入讲解

    2022-03-07 21:12:36
  • C#中利用LINQ to XML与反射把任意类型的泛型集合转换成XML格式字符串的方法

    2022-02-12 23:04:39
  • 在Winform程序中使用Spire.Pdf实现页面添加印章功能的实现

    2022-05-29 16:57:27
  • Android实现简单音乐播放器(MediaPlayer)

    2023-04-24 19:28:57
  • 浅谈Java字符串比较的三种方法

    2023-05-13 12:26:51
  • 解决Druid动态数据源配置重复刷错误日志的问题

    2021-06-06 17:44:51
  • 两种用空格分隔的java字符串的方式

    2023-10-01 05:51:04
  • OpenCV实现反阈值二值化

    2022-06-29 16:02:33
  • Java 线程池原理深入分析

    2023-01-30 19:59:43
  • 详解Java线程池如何统计线程空闲时间

    2022-11-09 07:41:10
  • 基于ElasticSearch Analyzer的使用规则详解

    2023-09-28 14:41:04
  • springboot 集成redission 以及分布式锁的使用详解

    2023-06-20 06:48:43
  • IntelliJ IDEA基于SpringBoot如何搭建SSM开发环境的步骤详解

    2022-11-24 12:10:39
  • WinForm调用jar包的方法分析

    2023-11-17 04:09:46
  • 如何设计一个安全的API接口详解

    2023-03-06 14:57:03
  • Springboot实现图片上传功能的示例代码

    2022-08-05 22:46:44
  • SpringBoot下使用MyBatis-Puls代码生成器的方法

    2023-11-25 17:07:07
  • SpringBoot中dubbo+zookeeper实现分布式开发的应用详解

    2023-09-13 19:04:45
  • asp之家 软件编程 m.aspxhome.com