Android自定义Lint增量代码检查工具

    xiaoxiao2022-07-07  209

    背景

    Lint是Google提供的一个静态代码检查工具,可以扫描出代码中潜在的问题,并且会对开发人员做出提示。而且除了Android原生提供的几百种Lint规则以外,还可以使用Lint框架的API自定义Lint规则。

    自定义Lint规则可以根据项目需求制定不同的扫描规则。比如:编码规范、代码风格、特定问题检查等。

    有了自定义检查规则,提交代码的时候可以规范代码编写。但是还有一个问题,新的代码使用新的规范,但如何解决项目中老代码风格?对于老代码,不可能每行代码都要修改。这个时候就要有针对性的检查,那就是使用增量检查。

    增量代码检查有以下几个好处

    避免修改“祖传”代码的一些问题。如果全量扫描,之前老代码的问题一大堆就会暴露出来, 这样就大大增加了工作量,开发人员也没有那么多的精力全部修改;增加了一些代码规范的强制性。增量扫描代码,如果代码有问题,就会滚,可以强制开发人 员规范代码;

    实现Lint增量代码检查工具

    这里Lint增量代码检查工具是以Gradle插件的方式实现的。只需要在项目中引用插件便可使用Lint工具。主要实现的功能是在git提交场景下,每次提交代码都会检查新增的代码(以行为单位)。如果代码中存在不符合Lint自定义规则的代码,就回滚本次提交。

    增量代码检查流程

    Lint增量代码检查工具使用git hooks(post-commit) + Lint框架实现。

    git hooks:是用来响应git的操作的脚本,相当于一个回调。执行特定的git操作会出发特定的git hooks脚本执行。

    Lint框架:是实现Lint扫描的基础。利用Lint框架提供的API执行Lint扫描。

    提交(git commit)本次修改代码(通过git diff命令找出提交的文件);触发git hooks(post-commit)脚本执行。在脚本中执行Lint检查任务(该任务是gradle任务)开始Lint检查;创建LintRequest(主要作用是指定Lint将要扫描的文件);获取增量代码(通过git diff找出修改的行号);开始Lint检查;检查完毕输出结果,如果有不符合规则的代码,将回退本次提交。

    Lint增量检查实现原理

    看完上面的流程,可能会觉得不明所以。这里针对各个步骤做出详细解析。在实现Lint增量代码检查的过程中,首要的步骤就是获取将要提交的增量代码。目前的解决方案是通过git命令获取相关数据。

    git hooks的执行

    目前的方案是通过使用post-commit脚本触发检查流程。post-commit脚本是在git commit之后执行。

    获取LInt检查的文件

    获取增量文件

    git diff --name-only --diff-filter=ACMRTUXB HEAD~1 HEAD~0

    通过git diff命令获取本次提交的文件。

    /** * 通过Git命令获取需要检查的文件 * * @param project gradle.Project * @return 文件列表 */ List<String> getCommitChange(Project project) { ArrayList<String> filterList = new ArrayList<>() try { //此命令获取本次提交的文件 在git commit之后执行 String command = "git diff --name-only --diff-filter=ACMRTUXB HEAD~1 HEAD~0" String changeInfo = command.execute(null, project.getRootDir()).text.trim() if (changeInfo == null || changeInfo.empty) { return filterList } String[] lines = changeInfo.split("\\n") return lines.toList() } catch (Exception e) { e.printStackTrace() return filterList } }

    获取增量代码

    这是关键的一步,因为这一步要获取增量代码所在的具体行号,通过使用这些行号数据实现增量检查的效果。

    git diff --unified=0 --ignore-blank-line --ignore-all-space HEAD~1 HEAD filepath

    filepath就是增量文件的相对路径。

    数据准备完毕以后,剩下的工作就要交给Lint框架了。接下来开始执行Lint检查操作。

    /** * 通过git diff获取已提交文件的修改,包括文件的添加行的行号、删除行的行号、修改行的行号 * * @param filePath 文件路径 * @param project Project对象 * @param startIndex 修改开始的下表数组 * @param endIndex 修改结束的下表数组 */ void getFileChangeStatus(String filePath, Project project, List<Integer> startIndex, List<Integer> endIndex) { try { String command = "git diff --unified=0 --ignore-blank-lines --ignore-all-space HEAD~1 HEAD " + filePath String changeInfo = command.execute(null, project.getRootDir()).text.trim() String[] changeLogs = changeInfo.split("@@") String[] indexArray for (int i = 1; i < changeLogs.size(); i += 2) { indexArray = changeLogs[i].trim().split(" ") try { int start, end String[] startArray = null if (indexArray.length > 1) { startArray = indexArray[1].split(",") } if (startArray != null && startArray.length > 1) { start = Integer.parseInt(startArray[0]) end = Integer.parseInt(startArray[0]) + Integer.parseInt(startArray[1]) } else { start = Integer.parseInt(startArray[0]) end = start + 1 } startIndex.add(start) endIndex.add(end) } catch (NumberFormatException e) { e.printStackTrace() startIndex.add(0) endIndex.add(0) } } } catch (Exception e) { e.printStackTrace() } }

    执行Lint检查

    Lint框架中的主要类说明:

    LintCliClient:Lint客户端,作用是集成lint检查的操作、相关配置以及lint检查的入口。LintCliFlags:Lint标志位管理类,提供了Lint操作的标志位。Lint代码检查工具主要使用了该类中生成日志的配置,通过加入不同实现的报告生成类可以实现不同的输出格式(比如TXT、XML、HTML等)。LintRequest:执行Lint操作时的一个请求类,主要作用是存储Lint将要扫描的文件。在Lint工具中重写LintRequest初始化方法可以实现增量文件的检查。LintDriver:执行Lint规则检查逻辑的类。IssueRegistry:自定义Lint规则管理类。用于添加Lint自定义规则。

    上述对于Lint框架中类的介绍是在实现Lint增量代码检查中主要用到的类。

    创建LintRequest

    class LintToolClient extends LintCliClient { @Override /** * 通过重写createLintRequest方法创建LintRequest */ protected LintRequest createLintRequest(List<File> files) { LintRequest request = super.createLintRequest(files) for (Project project : request.getProjects()) { for (File file : files) { project.addFile(file) } } return new LintRequest(this, files) } }

    上面的代码就是LintRequest的创建过程,通过重写LintCliClient中的createLintRequest方法。其中参数files就是将要检查的文件。

    Lint检查的执行逻辑

    /*LintCliClient*/ public int run(@NonNull IssueRegistry registry, @NonNull List<File> files) throws IOException { assert !flags.getReporters().isEmpty(); this.registry = registry; //Lint自定义检查规则 LintRequest lintRequest = createLintRequest(files); //创建LintRequest driver = createDriver(registry, lintRequest); //创建LintDriver addProgressPrinter(); validateIssueIds(); driver.analyze(); //执行Lint检查 Collections.sort(warnings); int baselineErrorCount = 0; int baselineWarningCount = 0; int fixedCount = 0; LintBaseline baseline = driver.getBaseline(); if (baseline != null) { baselineErrorCount = baseline.getFoundErrorCount(); baselineWarningCount = baseline.getFoundWarningCount(); fixedCount = baseline.getFixedCount(); } Stats stats = new Stats(errorCount, warningCount, baselineErrorCount, baselineWarningCount, fixedCount); boolean hasConsoleOutput = false; //根据LintCliFlags中的Reports打印Lint报告 for (Reporter reporter : flags.getReporters()) { reporter.write(stats, warnings); if (reporter instanceof TextReporter && ((TextReporter)reporter).isWriteToConsole()) { hasConsoleOutput = true; } } //............省略部分代码.............. return flags.isSetExitCode() ? (hasErrors ? ERRNO_ERRORS : ERRNO_SUCCESS) : ERRNO_SUCCESS; }

    上面代码的逻辑就是Lint执行检查的主要过程。可以看到,在代码中先传入自定义的Lint规则IssueRegistry,然后创建LintRequest,接下就开始执行Lint检查,最后将结果输出。结果输出到添加在LintCliFlags的Reports中。

    增量代码检查的实现

    //输出Lint检查报告 for (Reporter reporter : flags.getReporters()) { reporter.write(stats, warnings); if (reporter instanceof TextReporter && ((TextReporter)reporter).isWriteToConsole()) { hasConsoleOutput = true; } }

    根据上面代码的逻辑,是Lint检查输出结果的过程。增量代码检查的实现就是重写Reporter类,在重写的类中实现自定义的输出规则,这里的实现方法就是使用上文中通过git命令获取的文件修改行号进行过滤,从而实现增量检查的效果。

    总结

    上面描述了Lint增量代码检查工具的实现过程,实现增量代码检查的关键就是获取文件修改的精确位置,以便在输出结果是进行过滤。

    增量代码检查相较于常规的Lint检查,好处就是能够避免老代码的与新规则的冲突,同时结合git使用能够在提交代码时增加一些强制性。

    最后,Lint增量代码工具中使用的是Lint的自定义规则。这些还可以作为原生的Lint规则的扩展,在代码编写的阶段使用,效果跟原声Lint规则一致。

    对Lint增量代码工具的实现感兴趣的同学,可以在github上获取源码,感兴趣的可以star一下。

    Lint增量代码检查工具链接

    最新回复(0)