Lint实践一:自定义Lint规则

如果我做错了什么,请让我知道

作者 Moonshot 日期 2018-12-17
Lint实践一:自定义Lint规则

Lint实践一:自定义Lint规则

Lint介绍

Lint是Android提供的一个静态代码分析并找出其中的潜在问题的一个强大的工具。

在开发过程中AS常见IDE警告:一些硬编码的警告、未使用的代码等等都是Lint进行检测和提醒的。

还有一个常用的就是UnusedResources.查找不再使用的资源。

Android studio –>Analyze–>Run Inspection By Name->Unused resources

其他常见的静态分析工具还有FindBugs等,但Lint又一个不可替代的优势便是支持AS的实时检测和提醒,能在第一时间告诉你哪里错了,并提醒你修正。

本文主要介绍、如何编写自定义Lint,并在项目中集成的基本流程。

编写自定义Lint

####Lint已经很强大了,为什么还需要自定义lint呢

  • 代码规范:类名、方法名、文件名等命名规范。
  • 项目实践中常见的代码的危险写法(易奔溃、性能问题等),但原生Lint不支持

Lint基础Api

  • Issue:表示一个Lint规则。
  • IssueRegistry:用于注册要检查的Issue列表。自定义Lint需要生成一个jar文件,其Manifest指向IssueRegistry类。
  • Detector:用于检测并报告代码中的Issue。每个Issue包含一个Detector。
  • Scope:声明Detector要扫描的代码范围,例如Java源文件、XML资源文件、Gradle文件等。每个Issue可包含多个Scope。
  • Scanner:用于扫描并发现代码中的Issue。每个Detector可以实现一到多个Scanner。自定义Lint开发过程中最主要的工作就是实现Scanner。

####IssueRegistry

public class MoonshotIssueRegistry extends IssueRegistry {
@Override
public synchronized List<Issue> getIssues() { //自定义支持检测ISSUE列表
return Arrays.asList(
LogDetector.ISSUE,
ToastUtilsDetector.ISSUE,
XXDetector.ISSUE
);
}

@Override
public int getApi() {
//避免AS原生的Lint检测:ObsoleteLintCustomCheck
return com.android.tools.lint.detector.api.ApiKt.CURRENT_API;
}
}

Scanner 编写

  • UastScanner:扫描Java源文件、支持kotlin (JavaScanner / JavaPsiScanner 已废弃
  • XmlScanner:扫描XML文件
  • ClassScanner:扫描class文件
  • BinaryResourceScanner:扫描二进制资源文件
  • ResourceFolderScanner:扫描资源文件夹
  • GradleScanner:扫描Gradle脚本
  • OtherFileScanner:扫描其他类型文件

举个栗子:检测直接使用Log的Lint检测

public class LogDetector extends Detector implements Detector.UastScanner {
public static final Issue ISSUE = Issue.create(
"LogUse",
"避免直接使用Log",
"使用自定义Log工具类,防止在正式包打印log",
Category.SECURITY, 5, Severity.ERROR,
new Implementation(LogDetector.class, Scope.JAVA_FILE_SCOPE));

@Override
public List<String> getApplicableMethodNames() {
//要检测的方法名
return Arrays.asList("v", "d", "i", "w", "e", "wtf");
}

@Override
public void visitMethod(JavaContext context, JavaElementVisitor visitor, PsiMethodCallExpression call, PsiMethod method) {
if (context.getEvaluator().isMemberInClass(method, "android.util.Log")) {
//
String msg = "使用自定义Log工具类,防止在正式包打印log";
context.report(ISSUE, call, context.getLocation(call.getMethodExpression()), msg);
}
}
}

不详细讲具体其他的Api。但需要的时候去写自己会熟悉了,

自定义Lint检查编写核心是实现Scanner,但更加重要的事如何定义那些高危的代码。()

项目集成

自定义Lint的核心,在于编写的自定义Lint打包出来的lint.jar。但由于Android项目直接依赖并不能生效。

网上一般都是采用LinkedIn的方案: Writing Custom Lint Checks with Gradle

但是该配置有点老旧,下面是当前最新可行的配置,相比之前会LinkedIn原文的配置简单一些

创建Java工程LintCore 编写具体的Lint规则

gradle中配置

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
compileOnly 'com.android.tools.lint:lint-api:26.2.1'
compileOnly 'com.android.tools.lint:lint-checks:26.1.1'
}
jar {
manifest {
//指定成你定义的IssueRegistry
attributes("Lint-Registry-v2": "com.moonshot.lint.MoonshotIssueRegistry")
}
}

创建Android Library工程 LintLibrary

主要用来依赖lint.jar,打包成aar上传到maven

gradle中配置

dependencies {
lintChecks project(':lintCore')
}

LintLibrary打包成AAR (本地依赖或者上传带maven)

建议上传到maven,方便集成。(demo直接本地依赖了)

注意事项:不可直接implementation project(‘:LintLibrary’) 可以生效,但是AS不会实时检测。

增量检测、提交检测

  • 增量检测:一个老项目中的旧代码一定会存在不规范的情况,如果需要强制满足当前的lint规则,则需要一定的人力进行改动,且存在修改风险。所以应该只对新增的代码进行强制的Lint检查
  • git提交检测:不满足的Lint规则的代码不允许提交。

具体实现的下次再写了。。。