Kotlin代码规范那些事儿(detekt上手指南)

本文介绍了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命令即可。扫描结果即可在终端直接查看,并可以直接定位到问题代码处:
detekt_report_in_terminal
也可以在build/reprots/路径下查看输出的报告文件:
detekt_report_html

在子模块中应用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)
image-20210228105816869
其中的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_external_tool

绕过检查

通过配置文件可以开启或关闭某些规则,但如果仅仅想忽略某些文件中某些问题,而不是直接关闭这类问题的扫描,detekt提供了两种方式来绕过(原文是suppress issue,直译成压制问题,意译的话,可以解释为成绕过检查)。

第一种方式是使用@Suppress注解:

  • 在类名或者方法名前添加诸如@Suppress("LargeClass")这样的注解,可以有针对性地忽略这个文件中的某些告警。

  • 在文件头添加@file:Suppress("TooManyFunctions"),则可以直接告诉detekt不扫描这个文件中的这类问题。

第二种方式是通过基线文件,好处是不会因为添加了@Suppress注解而污染原代码。另一方面,对于臃肿的历史代码,可能没有时间来完全重构,如果直接添加@Suppress绕过整个文件,那么如果下次修改了这个文件,引入的新问题可能也会被绕过。

这种场景,就可以运行./gradew detektBaseline来生成一个baseline.xml基线文件。有了这个基线文件,下次扫描时,就会绕过文件中列出的基线问题,而只提示新增问题。

高亮问题

detekt还提供了IntelliJ插件,简单配置后,就可以在编辑器中高亮显示,代码中存在问题。便于我们在代码编写的过程中,留意存在的代码问题。
detekt_idea_plugin_config
高亮的效果和IDE自带的inspection的代码提示效果类似,鼠标移动到高亮的地方后会显示问题提示。
detekt_higglight

小结

  • 通过在工程中引入detekt工具,可以很方便的对kotlin代码执行静态扫描;
  • 通过对detekt提供的规则集进行修改,可以定义适合团队的代码规范;
  • 通过detekt提供的autoCorrect特性,可以快速地格式化代码;
  • 通过detekt提供的CLI接口,可以在IDE中对单个文件执行检查,实时反馈问题整改进度;
  • 通过@Suppress注解或者基线文件,来绕过检查;
  • 通过detekt提供的IntelliJ插件,可以在代码编写的过程中高亮提醒存在的代码问题;

可以说,detekt为我们提供了一套完整的kotlin代码规范治理方案。

总结

整改老代码是个费时费力的活,本文所讨论的detekt的使用方法,可以一定程度上提升整改的效率。此外,借助detekt这类静态代码扫描工具,也可以在平时开发的过程中,时刻反馈代码质量问题,避免潜在的代码缺陷。

这次也是借这“代码质量治理”这股东风,对项目历史代码进行了整改。但是,整改不仅仅是为了提高质量得分,更重要的是要提高代码规范的意识,在平时的开发过程中就注重写出规范的代码,如此,才能保证软件的质量。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值