资讯专栏INFORMATION COLUMN

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

MangoGoing / 769人阅读

摘要:检查的执行逻辑自定义检查规则创建创建执行检查根据中的打印报告省略部分代码上面代码的逻辑就是执行检查的主要过程。增量代码检查的实现输出检查报告根据上面代码的逻辑,是检查输出结果的过程。

背景

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 getCommitChange(Project project) {
    ArrayList 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("
")
        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 startIndex, List 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 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 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() ");

上面代码的逻辑就是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增量代码检查工具链接

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/7213.html

相关文章

  • 美团外卖Android Crash治理之路

    摘要:通过团队的全力全策,美团外卖的平均率从千分之三降到了万分之二,最优值万一左右率统计方式次数。美团外卖自年创建以来,业务就以指数级的速度发展。目前美团外卖日完成订单量已突破万,成为美团点评最重要的业务之一。 面试中常常问到的是Android的性能优化以及Crash处理。 今天我们来学习一下啊美团App的Crash处理。更多参考《Android性能优化:手把手带你全面实现内存优化》 原为地...

    elva 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<