聊聊我所知道的 Android 相关的代码检测
因为笔者业务开发能力不太够,所以会经常做一些周边的杂活。曾经做过在团队里建立代码检测机制的工作,所以我想简单地说下这部分的内容。
代码检测的地位
首先要说的是 软件开发的检测 的目的是什么,以及都有那些类型的检测。
在我看来,检测的目的就是为了 保证代码可以满足某些要求 。
以笔者浅显的见识,现在觉得一般的软件开发检测包括:
- 代码检测
- 业务功能的单元测试
- 模块整体(比如一个 aar / 一个 apk)的自动化测试
- 真人 QA 介入的测试
其中从上到下,
- 测试执行成本(设计成本、人工成本等)越来越大;
- 测试执行所用时间越来越长;
- 测试执行频率越来越低;
- 发现问题以后修复的成本越来越高;
今天只讨论其中 代码检测 这个环节,其余的环节因为笔者了解的并不多,就不在本次献丑了。
代码检测指的是什么?
笔者认为 代码检测 指的是开发者从 提出代码修改 到 初步确认代码没有简单的错误 的这个过程的。因为将 业务功能 相关的单元测试划分出去了,所以这部分 代码检测 是不包含业务功能的检测的。也就是说,这部分只进行
这样子说可能比较模糊,我举几个现在比较常见的检测规则:
- 改动是否包含魔数
- 改动里的变量名是否符合项目的要求(比如要求静态变量使用大写命名,属性变量使用驼峰格式命名)
- 改动是否导致文件过于庞大(比如说单个文件超过了 1200 行,很难让人读懂)
- 改动是否导致类继承层级过多(比如改完了以后当前类到 Activity 有五层继承关系)
- 改动是否导致某个方法过于复杂(比如单个方法行数超过了 150 行,某个方法里的 if / for / when 过多等)
- ……
这些检测,在一些手熟的开发者手中,是很难出现的,因为这些人大都会很注意程序的基本设计,会有意识避免这些会导致代码可读性变差的写法。不过对于一些项目比较着急、或者像笔者这样不太注意的开发者来说,就很容易写出这种代码。
这种代码其实在 Code Review 环节是很容易发现的,不过如果单纯依靠人来做这样的检测的话,一是比较慢,二是比较累。所以最好的方法就是增加自动检测的机制,如果不符合这些条件的话,可以直接提示出来进行改动。这样,通过检测的代码就有了一个最基本的质量底线。这也就是上面提到的 代码检测 了。从这个角度说,代码检测 其实是用来 1)规范代码基本写法 2)帮助 Code Review 的。
Android 上如何进行代码检测
对于开发者来说,开发上面这种检测插件无疑是很简单的事情。但是对于笔者来说,开发这些东西需要查看大量语法解析的资料,所以还是采用已经有的开源方案了。
对于 Android 上要进行的代码检测,有这几种可选的方案:
- 纯静态检测文本的方式解析代码
- 解析代码关系,检测代码
接下来分别介绍
纯静态检测代码
这种检测方式,类似于 python 这类“解析型语言”。特点是只需要输入一个 java 文件,不需要它所依赖的类的文件(比如说父类的类文件),即可以完成检测。甚至不需要整个文件,只输入其中的一个方法也可以进行检测!因为其只依赖文本内容的特征,所以笔者称之为静态检测代码。
这部分已有比较成熟的开源方案。其中对于 java 有大名鼎鼎的 check-style; 对于 kotlin 则有新秀检测方案 detekt 。两者的接入方式比较一样,使用方式也很类似。限于篇幅,具体的用法就不在此转发了,以官网的为准,各位朋友可以查看官网的使用说明。 虽然要对付的语言不一样,但是 check-style 和 detekt 如此相似,让人不由想看看其内部实现的方式。简单地查看了各自的源码后,会发现其实内部的原理都一样,只是表现的形式不同罢了。两者的思路其实大概流程都是下面这种流程(就不细说了,因为太详细的话,笔者也不是很明白了;而且这里的描述可能不够清楚,如果看不明白的话,可能是因为笔者的理解不对或者是表达能力有限,可以先不关注。)
基于输入的文件内容解析出来一个语法树。
会有多个写好的检测规则类,这些检测规则其实就是 java语法树遍历器 / kotlin 语法树遍历器。
距离:比如说检查文件
在生成的语法树上,遍历每一个元素:
对每一个元素,都遍历每一个规则。
上述的遍历完成后,即相当于每一个规则都遍历了该语法树,也即相当于对输入的文件进行了一遍遍历。各个规则也都完成了对文件的检测,采集到了文件不通过检测的部分。
最后将所有不通过检测的部分全都输出,也就完成了文本的检测了。 解析代码关系,检测代码
相比较于上面的类似 “解释型语言” ,这种检测方式更像 “编译型语言” 。这种检测方式只依靠单一的输入文件是无法完成的,需要输入的是一个可以作为整体进行“链接”的项目。比如 Android Studio 自带的 lint 就是这类检测方式。
举个例子说明:
- 假设需要禁止一个类的子类太过庞大(比如继承深度不能超过 5,每一代的继承自雷数量不能超过 6)。
这类涉及多个文件的检测,明显不是纯文本可以完成的,需要将其他的文件一并读取分析才可以完成判断。
这种检测因为更加复杂,运行一次的用时也会更多,差不多类似于进行一次项目的编译了。
但是因为笔者没有实际运用过 lint 进行复杂的代码检测,所以在这里不多谈了。
总结
代码检测是用来规范代码格式的,对于保证代码的最低质量还是比较有用的。当然了如果为了写出更好的代码,还是需要多多思考,少复制粘贴的。
|