使用 Kotlin 实现 Android 自定义 Lint 检查规则的步骤指南

一、创建 Lint 规则模块

  1. 新建 Android 库模块

    在项目中创建新的 Java/Kotlin Library 模块(非Android模块),例如命名为 custom-lint

  2. 配置 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
}

三、配置清单文件

  1. 在模块 src/main/resources/META-INF/ 下创建 services 目录

  2. 新建文件 com.android.tools.lint.client.api.IssueRegistry

  3. 文件内容:

    com.example.lint.CustomIssueRegistry


四、集成到项目

  1. 在应用模块的 build.gradle 中添加:
groovy 复制代码
dependencies {
    lintChecks project(':custom-lint')
}

五、测试规则

  1. 编写测试用例
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())
    }
}

六、发布规则(可选)

  1. 打包为 AAR/JAR
shell 复制代码
./gradlew :custom-lint:assemble
  1. 发布到 Maven 仓库:
kotlin 复制代码
// 在 custom-lint 的 build.gradle 中添加
publishing {
    publications {
        create<MavenPublication>("lint") {
            groupId = "com.example"
            artifactId = "custom-lint"
            version = "1.0.0"
            from(components["java"])
        }
    }
}

高级技巧

  1. 检测 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 作为尺寸单位")
        }
    }
}
  1. 快速修复支持
kotlin 复制代码
context.report(
    issue = ISSUE,
    location = context.getLocation(node),
    message = "问题描述",
    quickfixData = LintFix.create()
        .replace()
        .text("旧代码")
        .with("新代码")
        .build()
)

注意事项:

  1. 保持 Lint API 版本与 AGP 版本一致
  2. 使用 ./gradlew lint 进行测试
  3. 通过 lint.debug = true 启用调试日志
  4. 在 Android Studio 的 Preferences | Editor | Inspections 中启用自定义检查

通过以上步骤,您可以创建针对团队特定编码规范的自定义 Lint 规则,有效提升代码质量和一致性。

相关推荐
Jelian_1 小时前
SpringBoot自定义实体类字段的校验注解
java·spring boot·spring
一起搞IT吧5 小时前
Camera相机人脸识别系列专题分析之一:人脸识别系列专题SOP及理论知识介绍
android·图像处理·人工智能·数码相机
老神在在0016 小时前
javaEE1
java·开发语言·学习·java-ee
魔道不误砍柴功6 小时前
《接口和抽象类到底怎么选?设计原则与经典误区解析》
java·开发语言
feifeigo1236 小时前
Docker-compose 编排lnmp(dockerfile) 完成Wordpress
android·docker·容器
small_white_robot7 小时前
Tomcat- AJP协议文件读取/命令执行漏洞(幽灵猫复现)详细步骤
java·linux·网络·安全·web安全·网络安全·tomcat
图梓灵7 小时前
Maven与Spring核心技术解析:构建管理、依赖注入与应用实践
java·笔记·spring·maven
鸿蒙布道师7 小时前
HarmonyOS 5 应用开发导读:从入门到实践
android·ios·华为·harmonyos·鸿蒙系统·huawei
岁忧8 小时前
(nice!!!)(LeetCode 每日一题) 3372. 连接两棵树后最大目标节点数目 I (贪心+深度优先搜索dfs)
java·c++·算法·leetcode·go·深度优先
加什么瓦8 小时前
Java—多线程
java·开发语言