使用 Kotlin 实现 Android 自定义 Lint 检查规则的步骤指南
一、创建 Lint 规则模块
-
新建 Android 库模块
在项目中创建新的Java/Kotlin Library
模块(非Android模块),例如命名为custom-lint
-
配置
build.gradle.kts
plugins {id("java-library")id("org.jetbrains.kotlin.jvm") version "1.8.20" // 使用最新Kotlin版本
}dependencies {compileOnly("com.android.tools.lint:lint-api:30.4.0") // 根据AGP版本调整compileOnly("com.android.tools.lint:lint-checks:30.4.0")compileOnly(kotlin("stdlib"))
}java {sourceCompatibility = JavaVersion.VERSION_11targetCompatibility = JavaVersion.VERSION_11
}
二、实现自定义规则
1. 创建 Detector 类
import com.android.tools.lint.detector.api.*class ToastDetector : Detector(), Detector.UastScanner {override fun getApplicableMethodNames(): List<String> = listOf("makeText")override fun visitMethodCall(context: JavaContext,node: UCallExpression,method: PsiMethod) {val evaluator = context.evaluatorif (evaluator.isMemberInClass(method, "android.widget.Toast")) {// 检查是否调用了 show()val parent = node.parentif (parent !is UExpressionList || parent.uastParent !is UCallExpression ||(parent.uastParent as? UCallExpression)?.methodName != "show") {context.report(issue = ISSUE,location = context.getLocation(node),message = "必须调用 show() 方法显示 Toast")}}}companion object {val ISSUE = Issue.create(id = "ToastShowMissing",briefDescription = "缺少 Toast.show() 调用",explanation = "检测到 Toast.makeText() 但没有调用 show() 方法",category = Category.CORRECTNESS,priority = 6,severity = Severity.ERROR,implementation = Implementation(ToastDetector::class.java,Scope.JAVA_FILE_SCOPE))}
}
2. 创建 Issue Registry
@Suppress("UnstableApiUsage")
class CustomIssueRegistry : IssueRegistry() {override val issues: List<Issue>get() = listOf(ToastDetector.ISSUE// 添加更多检测规则)override val api: Int = CURRENT_API // 当前使用 14
}
三、配置清单文件
- 在模块
src/main/resources/META-INF/
下创建services
目录 - 新建文件
com.android.tools.lint.client.api.IssueRegistry
- 文件内容:
com.example.lint.CustomIssueRegistry
四、集成到项目
- 在应用模块的
build.gradle
中添加:
dependencies {lintChecks project(':custom-lint')
}
五、测试规则
- 编写测试用例
class LintTest : LintDetectorTest() {override fun getDetector(): Detector = ToastDetector()fun testMissingShow() {val code = """import android.widget.Toast;import android.content.Context;class Example {void test(Context context) {Toast.makeText(context, "test", Toast.LENGTH_SHORT);}}""".trimIndent()lint().files(java(code),// 添加必要依赖的stub文件*kotlin("""package android.widget;public class Toast {public static Toast makeText(Context c, String s, int d) { return null; }public void show() {}}""").indented()).issues(ToastDetector.ISSUE).run().expect("""|src/Example.java:6: Error: 必须调用 show() 方法显示 Toast [ToastShowMissing]| Toast.makeText(context, "test", Toast.LENGTH_SHORT);| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|1 errors, 0 warnings""".trimMargin())}
}
六、发布规则(可选)
- 打包为 AAR/JAR
./gradlew :custom-lint:assemble
- 发布到 Maven 仓库:
// 在 custom-lint 的 build.gradle 中添加
publishing {publications {create<MavenPublication>("lint") {groupId = "com.example"artifactId = "custom-lint"version = "1.0.0"from(components["java"])}}
}
高级技巧
- 检测 XML 布局
class XmlDetector : ResourceXmlDetector() {override fun getApplicableAttributes(): Collection<String> = listOf("textSize")override fun visitAttribute(context: XmlContext, attribute: Attr) {if (attribute.value.endsWith("sp")) {context.report(ISSUE, attribute, context.getValueLocation(attribute),"建议使用 dp 代替 sp 作为尺寸单位")}}
}
- 快速修复支持
context.report(issue = ISSUE,location = context.getLocation(node),message = "问题描述",quickfixData = LintFix.create().replace().text("旧代码").with("新代码").build()
)
注意事项:
- 保持 Lint API 版本与 AGP 版本一致
- 使用
./gradlew lint
进行测试 - 通过
lint.debug = true
启用调试日志 - 在 Android Studio 的
Preferences | Editor | Inspections
中启用自定义检查
通过以上步骤,您可以创建针对团队特定编码规范的自定义 Lint 规则,有效提升代码质量和一致性。