一、创建 Lint 规则模块
-
新建 Android 库模块
在项目中创建新的
Java/Kotlin Library
模块(非Android模块),例如命名为custom-lint
-
配置
build.gradle.kts
kotlin
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_11
targetCompatibility = JavaVersion.VERSION_11
}
二、实现自定义规则
1. 创建 Detector 类
kotlin
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.evaluator
if (evaluator.isMemberInClass(method, "android.widget.Toast")) {
// 检查是否调用了 show()
val parent = node.parent
if (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
kotlin
@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
中添加:
groovy
dependencies {
lintChecks project(':custom-lint')
}
五、测试规则
- 编写测试用例
kotlin
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
shell
./gradlew :custom-lint:assemble
- 发布到 Maven 仓库:
kotlin
// 在 custom-lint 的 build.gradle 中添加
publishing {
publications {
create<MavenPublication>("lint") {
groupId = "com.example"
artifactId = "custom-lint"
version = "1.0.0"
from(components["java"])
}
}
}
高级技巧
- 检测 XML 布局
kotlin
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 作为尺寸单位")
}
}
}
- 快速修复支持
kotlin
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 规则,有效提升代码质量和一致性。