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

一、创建 Lint 规则模块

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

  2. 配置 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_11
    targetCompatibility = 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.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
@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 中添加:
dependencies {
    lintChecks project(':custom-lint')
}

五、测试规则

  1. 编写测试用例
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
./gradlew :custom-lint:assemble
  1. 发布到 Maven 仓库:
// 在 custom-lint 的 build.gradle 中添加
publishing {
    publications {
        create<MavenPublication>("lint") {
            groupId = "com.example"
            artifactId = "custom-lint"
            version = "1.0.0"
            from(components["java"])
        }
    }
}

高级技巧

  1. 检测 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 作为尺寸单位")
        }
    }
}
  1. 快速修复支持
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 规则,有效提升代码质量和一致性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值