Lint是Google提供的一个静态代码检查工具,能够扫描出代码中潜在的问题,而且会对开发人员作出提示。并且除了Android原生提供的几百种Lint规则之外,还可使用Lint框架的API自定义Lint规则。git
自定义Lint规则能够根据项目需求制定不一样的扫描规则。好比:编码规范、代码风格、特定问题检查等。github
有了自定义检查规则,提交代码的时候能够规范代码编写。可是还有一个问题,新的代码使用新的规范,但如何解决项目中老代码风格?对于老代码,不可能每行代码都要修改。这个时候就要有针对性的检查,那就是使用增量检查。数组
增量代码检查有如下几个好处框架
这里Lint增量代码检查工具是以Gradle插件的方式实现的。只须要在项目中引用插件即可使用Lint工具。主要实现的功能是在git提交场景下,每次提交代码都会检查新增的代码(以行为单位)。若是代码中存在不符合Lint自定义规则的代码,就回滚本次提交。ide
Lint增量代码检查工具使用git hooks(post-commit) + Lint框架实现。工具
git hooks:是用来响应git的操做的脚本,至关于一个回调。执行特定的git操做会出发特定的git hooks脚本执行。post
Lint框架:是实现Lint扫描的基础。利用Lint框架提供的API执行Lint扫描。gradle
看完上面的流程,可能会以为不明因此。这里针对各个步骤作出详细解析。在实现Lint增量代码检查的过程当中,首要的步骤就是获取将要提交的增量代码。目前的解决方案是经过git命令获取相关数据。this
目前的方案是经过使用post-commit脚本触发检查流程。post-commit脚本是在git commit以后执行。编码
获取增量文件
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框架中类的介绍是在实现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一下。