本文介绍了kotlin代码扫描工具detekt的基本使用方法,并在此基础上总结了一套kotlin代码规范治理技巧。
引言
最近,团队在进行代码规范的治理,趁这个机会,调研了一下Kotlin语言的代码扫描工具的使用,摸索出了一套针对Kotlin语言的代码规范治理方案。
代码规范检查,除了在团队中推行CodeReview,更多地还是要依赖静态代码分析工具,来自动化地完成代码规范检查和整改。
类似于Java语言的checkstyle工具,kotlin也有两个类似的静态代码分析工具:
- ktlint:kotlin linter工具,可自动格式化代码。默认规则集扫描的都是代码格式问题。
- detekt:同样是针对kotlin语言的静态代码分析工具,除了代码格式问题(集成了Ktlint的功能),还能扫描出代码风格问题和潜在风险。
鉴于detekt涵盖了Ktlint的功能,因此直接选用detekt来作为代码扫描工具。
借助detekt治理kotlin代码
配置detekt
使用detekt的第一步是在Gradle工程中引入detekt插件。
接入detekt
正如官方文档中所介绍的,在gradle工程中使用detekt,只需3个步骤。
Step1:在工程根目录下的build.gralde文件中,引入detekt gradle plugin:
// root build.gradle
buildscript {
repositories {
jcenter()
}
}
// 引入detekt Gradle Plugin插件,并指定版本
plugins {
id("io.gitlab.arturbosch.detekt").version("1.16.0-RC1")
}
repositories {
jcenter()
}
Step2: 对detekt进行配置:
// root build.gradle
detekt {
input = files("src/main/kotlin", "src/main/java") // 指定需要扫描的源代码文件路径
config = files("config/detekt.yml") // 指定采用的规则集文件
reports { // 指定输出的报告文件类型
html {
enabled = true // Enable/Disable HTML report (default: true)
destination = file("build/reports/detekt.html") // Path where HTML report will be stored (default:
}
}
}
可配置的属性包括:指定输入的源代码文件,采用的规则集文件,输出的报告文件等。更多配置信息可以参看Options for detekt configuration closure。
Step3:运行./gradlew detekt
命令即可。扫描结果即可在终端直接查看,并可以直接定位到问题代码处:
也可以在build/reprots/
路径下查看输出的报告文件:
在子模块中应用detekt
对于包含多个子模块的工程来说,如果想要分模块对代码进行扫描,也可以在subprojects闭包引入detekt插件,这样就可以按需扫描不同的模块,而不用每次都扫描全部代码。
Step1:配置的时候,需要在子模块中引入detekt插件并配置:
// root build.gradle
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.1' // 注意AGP版本的兼容性
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10"
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.15.0" // 在这里添加detekt依赖
}
}
// 在这里为每个模块引入detekt扫描
subprojects {
apply plugin: "io.gitlab.arturbosch.detekt"
tasks.detekt.jvmTarget="1.8"
detekt {
config=files("$rootDir/config/detekt/detekt_config.yml")
reports {
html.enabled=true
}
}
}
注意,这里存在一个坑,最新的detekt 1.16.0-RC1只兼容AGP4.1+,如果使用更早版本的AGP,在subproject中引用detekt会报错,详见issue。
Step2:运行./gradlew detekt
会分模块扫描,也可以运行./gradlew app:detekt
只扫描app模块这个模块下的代码。结果报告也会在对应模块的build路径下生成。
制定规则集
在进行代码规范整改之前,还需要制定规范规则,这样才能有的放矢地进行代码整改。
如果没有在detekt闭包中指定config属性,detekt会使用默认的规则。这些规则采用yaml文件描述,运行./gradlew detektGenerateConfig
会生成config/detekt/detekt.yml
文件,我们可以在这个文件的基础上制定代码规范准则。
detekt.yml中的每条规则形如:
complexity: # 大类
active: true
ComplexCondition: # 规则名
active: true # 是否启用
threshold: 4 # 有些规则,可以设定一个阈值
# ...
更多关于配置文件的修改方式,请参阅官方文档-配置文件。
detekt的规则集划分为9个大类,每个大类下有具体的规则:
规则大类 | 说明 |
---|---|
comments | 与注释、文档有关的规范检查 |
complexity | 检查代码复杂度,复杂度过高的代码不利于维护 |
coroutines | 与协程有关的规范检查 |
empty-blocks | 空代码块检查,空代码应该尽量避免 |
exceptions | 与异常抛出和捕获有关的规范检查 |
formatting | 格式化问题,detekt直接引用的ktlint的格式化规则集 |
naming | 类名、变量命名相关的规范检查 |
performance | 检查潜在的性能问题 |
potentail-bugs | 检查潜在的BUG |
style | 统一团队的代码风格,也包括一些由detekt定义的格式化问题 |
更细节的规则说明,请参阅规则集说明 。
代码整改小技巧
在代码整改过程中,借助detekt提供的诸多功能,可以极大地提升效率。
自动格式化代码
对于formatting类别下的规则,都包含有autoCorrect这个属性选项,这表示是否需要在扫描代码的同时,自动对代码执行格式化。这个功能可以极大地方便大家整改与格式有关的代码问题。
要启用这个功能,还需要引入formatting插件,它是detekt提供的插件,打包了ktlint的功能,使用时配置如下:
detekt {
// ...
autoCorrect = true // 自动格式化代码的总开关
}
dependencies {
detektPlugins "io.gitlab.arturbosch.detekt:detekt-formatting:[version]"
}
在实际使用的时候,如果开启自动格式化的功能,运行时可能会报IndexOutOfBoundsException的错误,原因是格式化时会修改原文件,导致执行其它规则的扫描时产生异常,一种解决办法是,另外配置一个专门用于执行格式化的task,它所指定的规则集只开启format类别的规则:
// root build.gradle
subprojects {
apply plugin: "io.gitlab.arturbosch.detekt"
dependencies {
detektPlugins "io.gitlab.arturbosch.detekt:detekt-formatting:$detekt_version"
}
// 专门用于format的task
task detektFormat(type: io.gitlab.arturbosch.detekt.Detekt) {
description = "Reformat kotlin code."
config.setFrom(files("$rootDir/config/detekt/detekt_${detekt_version}_format.yml"))
setSource(files("$projectDir/src/main"))
autoCorrect = true
include("**/*.kt")
}
}
对单个文件执行检查
detekt除了可以在gradle中配置使用,还可以通过CLI命令行的方式使用。下载detekt-cli-1.15.0-all.jar就可以通过运行java -jar
的方式在终端中运行detekt。其它版本可以从此下载。如果还需要扫描格式化相关规则,还需要引入detekt-formatting-1.15.0.jar。
基于这个特性,我们可以在AS中配置External Toos,就可以在对编辑器当前打开的文件执行detekt扫描,这样可以方便我们在整改代码时,实时查看整改进度,而不用每次执行全局扫描,有助于提升整改代码的效率。
Step1:配置(Preferences > Tools > External Toos)
其中的CLI参数配置配置如下
-jar /path/to/detekt-cli-1.15.0-all.jar # detekt-cli-1.15.0-all.jar所在路径
-c /path/to/detekt_1.15.0_format.yml # 规则文件所在路径
--plugins /path/to/detekt-formatting-1.15.0.jar # detekt-cli-1.15.0-all.jar所在路径
-ac # 开启自动格式化
-i $FilePath$ # 需要扫描的源文件,这里直接使用AS提供的$FilePath$,表示当前编辑器所打开的文件
Step2:在已打开的文件空白处,点击右键菜单 > External Tools > detekt_formater即可运行,结果会在控制台中输出。
绕过检查
通过配置文件可以开启或关闭某些规则,但如果仅仅想忽略某些文件中某些问题,而不是直接关闭这类问题的扫描,detekt提供了两种方式来绕过(原文是suppress issue,直译成压制问题,意译的话,可以解释为成绕过检查)。
第一种方式是使用@Suppress注解:
-
在类名或者方法名前添加诸如
@Suppress("LargeClass")
这样的注解,可以有针对性地忽略这个文件中的某些告警。 -
在文件头添加
@file:Suppress("TooManyFunctions")
,则可以直接告诉detekt不扫描这个文件中的这类问题。
第二种方式是通过基线文件,好处是不会因为添加了@Suppress注解而污染原代码。另一方面,对于臃肿的历史代码,可能没有时间来完全重构,如果直接添加@Suppress绕过整个文件,那么如果下次修改了这个文件,引入的新问题可能也会被绕过。
这种场景,就可以运行./gradew detektBaseline
来生成一个baseline.xml
基线文件。有了这个基线文件,下次扫描时,就会绕过文件中列出的基线问题,而只提示新增问题。
高亮问题
detekt还提供了IntelliJ插件,简单配置后,就可以在编辑器中高亮显示,代码中存在问题。便于我们在代码编写的过程中,留意存在的代码问题。
高亮的效果和IDE自带的inspection的代码提示效果类似,鼠标移动到高亮的地方后会显示问题提示。
小结
- 通过在工程中引入detekt工具,可以很方便的对kotlin代码执行静态扫描;
- 通过对detekt提供的规则集进行修改,可以定义适合团队的代码规范;
- 通过detekt提供的autoCorrect特性,可以快速地格式化代码;
- 通过detekt提供的CLI接口,可以在IDE中对单个文件执行检查,实时反馈问题整改进度;
- 通过@Suppress注解或者基线文件,来绕过检查;
- 通过detekt提供的IntelliJ插件,可以在代码编写的过程中高亮提醒存在的代码问题;
可以说,detekt为我们提供了一套完整的kotlin代码规范治理方案。
总结
整改老代码是个费时费力的活,本文所讨论的detekt的使用方法,可以一定程度上提升整改的效率。此外,借助detekt这类静态代码扫描工具,也可以在平时开发的过程中,时刻反馈代码质量问题,避免潜在的代码缺陷。
这次也是借这“代码质量治理”这股东风,对项目历史代码进行了整改。但是,整改不仅仅是为了提高质量得分,更重要的是要提高代码规范的意识,在平时的开发过程中就注重写出规范的代码,如此,才能保证软件的质量。