Administrator
发布于 2026-08-25 / 33 阅读
0
0

系列文章《Android 工程师的 100 篇进阶笔记》第 50 篇:Android 代码质量保障:Lint、ktlint 与自定义规则

上一篇聊了 Lifecycle 源码,这篇聊聊代码质量保障工具链。代码质量不是靠 code review 人肉盯出来的,而是靠工具在 CI 和 IDE 层面拦截。Lint、ktlint、detekt 三件套配合好了,大部分低级问题在提交前就挡住了。

三种工具的分工

工具

检查范围

侧重点

Android Lint

Android 特有问题

资源、API 兼容性、性能

ktlint

Kotlin 代码风格

格式、命名、import

detekt

Kotlin 代码质量

复杂度、坏味道、潜在 bug

三者不冲突,互补使用。

Android Lint

Lint 是 Android SDK 自带的静态分析工具,检查 Android 特有问题。

基础使用

./gradlew lintDebug

报告生成在 app/build/reports/lint-results-debug.html。

常用规则

  • HardcodedText:硬编码字符串(应该用资源文件)

  • UsingWebView:WebView 未设置安全配置

  • ObsoleteLintCustomCheck:过时的 Lint 规则

  • UnusedResources:未使用的资源文件

  • IconLauncherShape:启动图标问题

  • NewApi:使用了高于 minSdk 的 API

配置

// build.gradle
android {
    lint {
        abortOnError = false  // 发现错误不中断构建
        warningsAsErrors = false
        disable += "HardcodedText" // 禁用某条规则
        enable += "UnknownNullness"
        baseline = file("lint-baseline.xml") // 基线文件
    }
}

Baseline 文件

存量项目加 Lint 会报几百个问题,不可能一次性全修。用 baseline 把当前问题"固定"下来,只检查新增问题:

./gradlew lintDebug --update-baseline

生成 lint-baseline.xml 后提交到仓库,后续只报告 baseline 之外的新问题。

ktlint

ktlint 检查 Kotlin 代码风格,规则来自官方 Kotlin Coding Conventions。

集成

// build.gradle (root)
plugins {
    id("org.jlleitschuh.gradle.ktlint") version "12.1.1" apply false
}

// build.gradle (app)
plugins {
    id("org.jlleitschuh.gradle.ktlint")
}

使用

./gradlew ktlintCheck     # 检查
./gradlew ktlintFormat    # 自动修复

ktlintFormat 能自动修复大部分格式问题(缩进、import 排序、尾随空格等),非常方便。建议在 git pre-commit hook 里跑 ktlintFormat。

自定义规则

// .editorconfig
[*.{kt,kts}]
ktlint_standard_no-wildcard-imports = enabled  # 禁止通配符 import
ktlint_standard_function-naming = disabled     # 禁用函数命名规则
max_line_length = 120

通过 .editorconfig 文件控制规则开关,不需要改 Gradle 配置。

detekt

detekt 检查 Kotlin 代码质量,比 ktlint 更深入:复杂度、坏味道、潜在 bug。

集成

// build.gradle (root)
plugins {
    id("io.gitlab.arturbosch.detekt") version "1.23.6" apply false
}

// build.gradle (app)
plugins {
    id("io.gitlab.arturbosch.detekt")
}

detekt {
    config.setFrom("$rootDir/config/detekt.yml")
    buildUponDefaultConfig = true
    parallel = true
}

配置文件

# config/detekt.yml
complexity:
  LongMethod:
    threshold: 60  # 方法超过 60 行报警
  LongParameterList:
    functionThreshold: 6  # 参数超过 6 个报警
  CyclomaticComplexMethod:
    threshold: 15

style:
  ReturnCount:
    max: 3  # return 语句超过 3 个报警
  WildcardImport:
    active: true

naming:
  FunctionNaming:
    functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)'

自定义规则

detekt 支持自定义规则,通过实现 Rule 类:

class NoLogDirectCallRule(config: Config) : Rule(config) {
    override val issue = Issue(
        id = "NoLogDirectCall",
        severity = Severity.Warning,
        description = "Don't call android.util.Log directly, use AppLog",
        debt = Debt.FIVE_MINS
    )

    override fun visitImportDirective(importDirective: KtImportDirective) {
        if (importDirective.importedFqName?.asString() == "android.util.Log") {
            report(CodeSmell(issue, Entity.at(importDirective), issue.description))
        }
    }
}

自定义规则打包成 jar 插件,在 detekt.yml 中启用:

NoLogDirectCall:
  active: true

Git Pre-commit Hook

#!/bin/bash
# .git/hooks/pre-commit

# 只检查暂存区的文件
files=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.kt$|\.kts$')

if [ -n "$files" ]; then
    echo "Running ktlintFormat..."
    ./gradlew ktlintFormat -PinternalPreCommit=true --daemon > /dev/null 2>&1
    
    # 重新暂存格式化后的文件
    echo "$files" | xargs git add
fi

或者用 ktlint 的 Git Hook 插件自动安装:

plugins {
    id("org.jlleitschuh.gradle.ktlint-idea") version "12.1.1"
}
./gradlew addKtlintCheckGitPreCommitHook

CI 集成

# GitHub Actions
- name: Run Lint
  run: ./gradlew lintDebug

- name: Run ktlint
  run: ./gradlew ktlintCheck

- name: Run detekt
  run: ./gradlew detekt

- name: Upload reports
  if: always()
  uses: actions/upload-artifact@v4
  with:
    name: code-quality-reports
    path: |
      app/build/reports/lint-results-*.html
      app/build/reports/detekt/detekt.html

实际效果

在一个 50 万行代码的项目上接入三件套后的统计:

  • Lint 发现 120 个问题(20% NewApi,30% 硬编码文本,50% 其他)

  • ktlint 修复了 300+ 格式问题

  • detekt 发现 15 个复杂度过高的方法、5 个潜在空指针

接入后新代码的质量明显提升,review 时不再纠结格式问题,聚焦逻辑和架构。

小结

代码质量保障三件套:Lint 管 Android 特性、ktlint 管格式、detekt 管质量。配合 baseline 文件做存量管理、pre-commit hook 做增量拦截、CI 做门禁,形成"开发-提交-CI"三层防线。投入一天配置,长期受益。


这是系列文章第 50 篇,Android 100 篇进阶笔记已完成一半。后续将继续深入:编译器插件、字节码插桩、热修复原理、Flutter 与 Android 互操作等更硬核的主题。


评论