静态代码检查 - Android Lint 自定义检查规则


上一篇文章我们了解到为何要使用Lint及自定义Lint规则;在创建我们自定义Lint工程前,我们先来了解下如何将自定义规则添加到Lint流程中。

将自定义规则添加到Lint流程

参考Google提供的Lint Sample:

  1. 先将编写好的规则打包成jar形式
  2. 使用gradle api lintPublish将jar打包到aar文件中
  3. 在项目中依赖此aar

所以我们需要创建一个包含java-library和android.library工程,用来生成lint规则及提供给项目使用。

创建Lint工程

前置环境:
Gradle Version: gradle-8.0-bin
build:gradle:8.0.2
Lint Version:com.android.tools.lint:lint-checks:30.0.3

版本不同的Lint在规则上都有些不同,我们以30.0.3版本进行规则自定义

创建 java-library Module并配置

  1. 创建 java-library Module lint-jar:
    在这里插入图片描述
  2. 在lint-jar中添加如下代码:
    在这里插入图片描述
    在这里插入图片描述
    getIssues方法返回的List就是设置的规则集合

编写规则Detector

举个简单的例子,当我们使用Message时,为了减少内存开销,我们希望复用Message池中Message实例,避免重复Message创建对象:
1.创建MessageDetector.java,继承Detector并实现Detector.UastScanner,代码如下:

//为了扫描Java源文件,实现Detector.UastScanner接口,通过实现不同的接口,扫描不同的文件类型
public class MessageDetector extends Detector implements Detector.UastScanner { //注释1
    //注释2
    public static final Issue ISSUE = Issue.create(
            "Message_Obtain_Tip",
            "建议使用handler.obtainMessage()或者Message.obtain()方法",
            "为了减少内存开销,不应该直接使用{new Message()},"
                    + "应该使用 {handler.obtainMessage()方法} 或者 {Message.obtain()方法},"
                    + "这样的话从整个Message池中返回一个新的Message实例,从而能够避免重复Message创建对象,减少内存开销.",
            Category.PERFORMANCE,
            5,
            Severity.ERROR,
            new Implementation(MessageDetector.class, Scope.JAVA_FILE_SCOPE)
    );


    @Nullable
    @Override
    public List<String> getApplicableConstructorTypes() { //注释3
        return Collections.singletonList("android.os.Message");
    }

    @Override
    public void visitConstructor(@NotNull JavaContext context,
                                 @NotNull UCallExpression node, @NotNull PsiMethod constructor) {//注释4
        context.report(ISSUE, node, context.getLocation(node), "避免直接使用new Message()");
    }
}

2.将MessageDetector.ISSUE添加到Issues里:

public class ILintIssueRegistry extends IssueRegistry {
    private final List<Issue> mIssues = new ArrayList<>();

    @NotNull
    @Override
    public List<Issue> getIssues() {
        mIssues.add(MessageDetector.ISSUE);
        return mIssues;
    }
}

3.在lint-aar工程里,将lint-jar打包到aar中:

dependencies {
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    lintPublish project(':lint-jar')
}

4.在目标工程中引入aar即可将自定义规则加入的Lint检查中:

dependencies {

    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    implementation project(":lint-aar")
}

执行命令"./gradlew lint",生成的结果就包含此规则:
在这里插入图片描述
关于注释:
注释1 --实现不同的Scanner接口会对相应的文件进行扫描,常用的方法如下,具体作用请查看代码注释:

    interface UastScanner : com.android.tools.lint.detector.api.SourceCodeScanner
    interface ClassScanner : com.android.tools.lint.detector.api.ClassScanner
    interface BinaryResourceScanner : com.android.tools.lint.detector.api.BinaryResourceScanner
    interface ResourceFolderScanner : com.android.tools.lint.detector.api.ResourceFolderScanner
    interface XmlScanner : com.android.tools.lint.detector.api.XmlScanner
    interface GradleScanner : com.android.tools.lint.detector.api.GradleScanner
    interface OtherFileScanner : com.android.tools.lint.detector.api.OtherFileScanner

注释2 --创建了一个Issue对象,包含了这个Issue的基本信息:
在这里插入图片描述

参数id 唯一的id,简要表面当前提示的问题。
参数briefDescription 简单描述当前问题
参数explanation 详细解释当前问题和修复建议
参数category 问题类别
参数priority 优先级,从1到10,10最重要
参数Severity 严重程度:FATAL(崩溃), ERROR(错误), WARNING(警告),
	INFORMATIONAL(信息性),IGNORE(可忽略)
参数Implementation Issue和哪个Detector绑定,以及声明检查的范围。Scope有如下选择范围:
	RESOURCE_FILE(资源文件),BINARY_RESOURCE_FILE(二进制资源文件),
	RESOURCE_FOLDER(资源文件夹),ALL_RESOURCE_FILES(所有资源文件),
	JAVA_FILE(Java文件), ALL_JAVA_FILES(所有Java文件),
	CLASS_FILE(class文件), ALL_CLASS_FILES(所有class文件),
	MANIFEST(配置清单文件), PROGUARD_FILE(混淆文件),JAVA_LIBRARIES(Java库),
	 GRADLE_FILE(Gradle文件),PROPERTY_FILE(属性文件),
	 TEST_SOURCES(测试资源),OTHER(其他);

与生成的结果信息对应

注释3 --扫描构造函数的类:
不同规则会使用不同的接口,可以参考google提供的默认规则进行开发

注释4 --扫描到对应的代码段时触发:
在这里生成report信息

这样我们就完成Lint规则的自定义,下篇将介绍如何进行增量编译及编码成Plugin使用.

拓展:

1.检查结果
对于静态检查扫描出来的结果,我们可以在各个应用模块的"/build/reports"下找到:
在这里插入图片描述
2.分析报告
打开文件夹后,拖拽lint-results-release.html文件到浏览器中进行查看,打开文件内容如下:
在这里插入图片描述

标号1 是规则的所检查的内容分类,Lint规则检查代码的正确性(Correctness)、安全性(Security)、性能(Performance)、易用性(Usability)、无障碍性(Accessibility)和国际化(Internationalization) 六大类;
标号2规则的ID,点击后会跳转到相应问题;
标号3 是规则的 简要描述
标号4 是扫描代码违反这条规则的个数
标号5 是规则的严重性,红色感叹号代表Error&Fatal,是我们需要特别关注的;黑色感叹号代表Warning

3.重点关注的规则
对于所有的Error及Fatal都需要解决掉,这类问题可能会引起严重的Bug,其次我们也应该关注性能(Performance)、安全性(Security)这两类问题,这可能会对系统的性能及稳定性造成影响。

参考文献:

自定义 Lint 检查实践指南
Android Lint API Guide
Android代码检查规则Lint的自定义与应用详解

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值