在 上一篇文章我们了解到为何要使用Lint及自定义Lint规则;在创建我们自定义Lint工程前,我们先来了解下如何将自定义规则添加到Lint流程中。
将自定义规则添加到Lint流程
参考Google提供的Lint Sample:
- 先将编写好的规则打包成jar形式
- 使用gradle api lintPublish将jar打包到aar文件中
- 在项目中依赖此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并配置
- 创建 java-library Module lint-jar:
- 在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的自定义与应用详解