使用 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 规则,有效提升代码质量和一致性。

相关推荐
darling_user41 分钟前
Android14 耳机按键拍照
android
真实的菜2 小时前
适配器模式:接口转换的神奇魔法[特殊字符],让不兼容的类和谐共处!
java·适配器模式
骚戴2 小时前
SpringBoot源码解析(十五):spring-boot-autoconfigure.jar的模块化设计
java
YuTaoShao2 小时前
Java八股文——计算机网络「应用层篇」
java·网络·计算机网络
Mryan20052 小时前
Android 应用多语言与系统语言偏好设置指南
android·java·国际化·android-studio·多语言
鲁Q同志3 小时前
若依导出模板时设置动态excel下拉框(表连接的)
java·excel
刘大浪3 小时前
uniapp 实战新闻页面(一)
android·uni-app
水沝淼燚4 小时前
kmp的实际使用1,开发android项目和native转kotlin开发
android
汇匠源4 小时前
Java 零工市场小程序 | 灵活就业平台 | 智能匹配 | 日结薪系统 | 用工一站式解决方案
java·小程序
CYRUS_STUDIO4 小时前
破解 VMP+OLLVM 混淆:通过 Hook jstring 快速定位加密算法入口
android·算法·逆向