静态代码审查

article/2025/9/15 19:27:07

本篇介绍静态代码审查的意义以及如何在Android studio中集成它们。需要注意的是,这些工具不是万能的,虽然它们能高效且全面地执行代码检查工作,但它们并不具备人类的“逻辑思维”优势。也就是说,静态代码审查工具是无法确保程序逻辑表达上的正确性的。除此之外,代码中的不安全(如某些条件下的死循环、空指针异常等)、代码的执行效率甚至编码风格、变量命名等都可以被静态代码审查工具检测出来。

  • 静态代码审查的意义
    静态代码审查可以说是整个软件开发过程中不可缺少的环节,但目前仍有很多公司忽视它。实际上,这种代码审查比动态测试(指通过运行被测程序、检查其运行结果是否符合预期,并符合运行效率和健壮性等要求的测试)更有效率。根据项目自身情况的不同,静态代码审查可以找到30%~70%的代码缺陷。
    静态代码审查通常在编译和进行动态测试之前进行,这样做能在产品正式发布之前发现缺陷,大大降低维护成本,被检查代码覆盖率高。同时,这种审查通过会花费较长时间,并需要由对项目代码有足够了解的工程师处理。

  • 安装静态代码审查工具
    审查工具有很多,例如Google官方的android lint、checkstyle、spotbugs以及pmd。本篇文章,我们主要介绍下android lint工具的使用。

  • Java静态代码分析工具对比

代码缺陷分类示例CheckStyleFindBugsPMDJtest
引用操作空指针引用
对像操作对像比较(使用==,而非equals)
表达式复杂化多余的if语句
数组使用数组下标越界
未使用变量或代码段未使用变量
资源回收I/O未关闭
方法调用未使用方法返回值
代码设计空的try/catch/finally块

使用Android Lint进行代码审查

Android Studio 提供了一个名为 lint 的代码扫描工具,可帮助您发现并更正代码结构质量方面的问题,而无需您实际执行应用,也不必编写测试用例。系统会报告该工具检测到的每个问题并提供问题的描述消息和严重级别,以便您可以快速确定需要优先进行的关键改进。此外,您还可以降低问题的严重级别以忽略与项目无关的问题,或者提高严重级别以突出特定问题。
Link Check的问题按严重程度分为5种,分别为Fatal、Error、Warning、Information和Ignore,对Issue忽略操作本质就是降低该Issue的严重程度为Ignore。

lint 工具可以检查您的 Android 项目源文件是否有潜在的 bug,以及在正确性、安全性、性能、易用性、无障碍性和国际化方面是否需要优化改进。使用 Android Studio 时,无论何时构建应用,都会运行配置的 lint 和 IDE 检查。不过,您也可以手动运行检查或从命令行运行 lint。
在这里插入图片描述
App源代码指整个工程的源代码文件,除了Java、Kotlin、XML外,还包括应用图标素材文件以及用于代码混淆的ProGuard配置文件。lint.xml文件是Lint检查的配置文件,当我们需要自定义检查规则时,通常会编辑这个文件。

运行Lint检查

查看Lint所有规则命令:

lint-list和lint-show

如果您使用的是 Android Studio 或 Gradle,您可以在项目的根目录下输入以下某个命令:

./gradlew lint

如果只需要对某个Debug版本或者Release版本进行代码检查,则可以使用

./gradlew lintDebug或者./gradlew lintRelease

随着程序逻辑日益复杂,代码量日益增多,我们还有可能需要只审查部分代码,或者采用不同的配置文件。在这种情况下,使用集成在Android Studio中的Lint工具更加方便。
在这里插入图片描述
在这里插入图片描述
默认情况下,检查的项目和在命令行启动检查的项目大体一致。不同的是,在Android Studio中,执行代码检查的结果需要在Inspect Results视图中查看。
在这里插入图片描述

自定义Lint检查范围

接下来,我们来探讨如何自定义Lint的检查范围。在Android Studio中,已经预设了很多检查范围供我们选择,为方便扩展和自定义检查需求,还开放了自定义Lint检查范围,主要通过广泛使用的Scope窗口实现。

  1. 使用预置的检查范围
    在这里插入图片描述
    从图中可以看到,由多个菜单项可以选择,我们先了解下它们都是些什么含义。
  • Project Files: 当前工程的所有文件。
  • Proiect Source Files: 当前工程的所有源代码文件。
  • Project Production Files: 当前工程中的所有生产文件。
  • Project Test Files: 当前工程中的所有测试文件。
  • Scratches and Consoles: 提供了两种临时的文件编辑环境,通常用来存放文本或代码片,该选项在实际开发中很少用到。
  • Module ‘app’:仅app模块的文件。
    Class Hierarchy:当我们选取这个菜单项并单击OK按钮时,会弹出新的窗口,窗口里显示当前工程中的所有类。我们可以使用窗口里的搜索功能过滤要检查的类。在未过滤的情形下,Lint会检查所有类。
  1. 创建自定义检查范围
    当预设的检查范围无法满足我们的自定义需求时,可以进一步对代码检查范围进行自定义。
    单击上图中下拉菜单右侧的三个点按钮,弹出Scope窗口,我们在这里配置自定义检查范围。
    在这里插入图片描述
    默认情况下,Scope配置为空。单击界面左上角的“+”,然后选在Local来添加新范围。
    Local和Share的共同点是给定代码检查范围,区别是Share还可用于具有范围字段的其他项目。也就是说,Local类型的Scope配置是个人使用的,保存在个人的workspace中,默认保存在/config/projects/<project_name>/.idea/workspace.xml中;Share类型的Scope配置是整个工程的,可以通过版本控制系统被团队成员共享,它的默认路径在/config/projects/<project_name>/.idea/scopes/中。
    此处我们以Local类型的Scope为例进行介绍,名称为View,即仅检查和UI视图相关的类。
    在这里插入图片描述
    如果读者对正则表达式比较熟悉,那么完全可以直接填写合法的正则表达式,达到定义范围的目的,或者使用窗口右侧的4个按钮来控制检查范围,分别说明如下:
  • Include:包含此文件夹及其文件,不包含于文件夹中的内容。
  • Include Recursively:包含此文件夹及其文件,递归包含所有自文件夹及其文件。
  • Exclude:排除此文件夹及其文件,不递归排除所有子文件夹及其文件。
  • Exclude Recursively:排除此文件夹及其文件,递归排除所有子文件夹及其文件。

自定义Lint检查类型

  1. 使用和自定义Lint配置文件
    在Android Lint中内置了多种静态代码检查的配置文件。我们可以直接使用它们,也可以更改它们的名称、说明、范围甚至是严重级别,也可以随时启用活禁用某个配置文件,达到跳过某种检查的目的。
    在这里插入图片描述
    在这里插入图片描述
    检查的项目、说明以及启用状态等信息一目了然地罗列在这个窗口中。我们除了可以通过复选框启用/禁用某个检查外,还需关注一个地方,就是位于左上角的Profile下拉菜单。
    展开Profile下拉菜单,默认情况下,预置了Default和project Default两个配置。显而易见,前者是对整个Android Studio而言的,将影响所有的过程;后者是对单个工程而言的。在未做任何自定义配置前,二者是一样的。
    同时,我们还可以单击下拉菜单右侧的小齿轮,添加更多自定义的菜单项。由Default复制而来的配置依旧影响所有工程,由Project Default复制而来的配置则仅对当前工程有效。当然,我们还可以对其进行重命名、删除、导入以及导出操作。
  2. 配置lint.xml文件
    通过前面的学习,我们已经可以实现特定范围,特定检查种类的自定义了。看上去似乎已经满足了静态代码检查的需要,事实上也确实如此。那为什么这里还要介绍limt xmnl 文件呢?
    想象这样一种情况,假设我们的工程有当个 XML 布局文件,要求某个或某几个布局文件需要单独定义检查类型。根据现有的知识,我们需要定义不止一个 scope,然后定义不止一个profile,最后挨个启动检查。是不是很烦项?有没有办法简化呢?答案是肯定的——借助lint.xml 定义规则,即可完成快捷方便的检查。
    lint xml位于整个工程根目录下,默认不会自动创建,需要我们手动添加这个文件,格式遵循标准的XML,一个规则定义的范例如下:
<?xml version="1.0" encoding="UTF-8"?>
<lint><!-- Disable the given check in this project --><issue id="IconMissingDensityFolder" severity="ignore" /><!-- Ignore the ObsoleteLayoutParam issue in the specified files --><issue id="ObsoleteLayoutParam"><ignore path="res/layout/activation.xml" /><ignore path="res/layout-xlarge/activation.xml" /></issue><!-- Ignore the UselessLeaf issue in the specified file --><issue id="UselessLeaf"><ignore path="res/layout/main.xml" /></issue><!-- Change the severity of hardcoded strings to "error" --><issue id="HardcodedText" severity="error" />
</lint>

可见,lint.xml文件由封闭的<lint>标记包裹,其中包含多个<issue>子原生定义了唯一id。

  1. 在源代码文件中添加忽略项
    除了上述在lint.xml中定义检查规则外,我们还可以直接在源代码中添加指定的忽略规则,支持,java、kotlin和xml三种类型的源代码。
    具体可参考下面链接:
    配置 Java、Kotlin 和 XML 源文件的 lint 检查

  2. 在整个module中添加忽略项
    在某些情况下,整个工程可能包含多于一个module,统一的检查规则可能不适用于所有module。因此,我们需要一种方法对单个module进行规则定义,秘诀就在于每个module的build.gradle文件。
    要定义某个module的检查规则是很容易的,只需在android节点下添加lintOptions代码块即可。

android {...lintOptions {// Turns off checks for the issue IDs you specify.disable("TypographyFractions")disable("TypographyQuotes")// Turns on checks for the issue IDs you specify. These checks are in// addition to the default lint checks.enable("RtlHardcoded")enable("RtlCompat")enable("RtlEnabled")// To enable checks for only a subset of issue IDs and ignore all others,// list the issue IDs with the 'check' property instead. This property overrides// any issue IDs you enable or disable using the properties above.checkOnly("NewApi", "InlinedApi")// If set to true, turns off analysis progress reporting by lint.quiet = true// If set to true (default), stops the build if errors are found.abortOnError = false// If true, only report errors.ignoreWarnings = true// If true, lint also checks all dependencies as part of its analysis. Recommended for// projects consisting of an app with library dependencies.isCheckDependencies = true}
}
...

自定义Lint规则

首先新建一个java依赖库,然后在build.gradle文件种添加如下脚本:

apply plugin: 'java-library'dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])compileOnly 'com.android.tools.lint:lint-api:30.0.0'//必须是compileOnlycompileOnly 'com.android.tools.lint:lint-checks:30.0.0'//必须是compileOnly
}
jar {manifest {attributes("Lint-registry-v2": "com.brett.lintcheck.SSIssueRegistry")}
}sourceCompatibility = "1.8"
targetCompatibility = "1.8"

继承IssueRegistry类:

package com.brett.lintcheck;import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.detector.api.Issue;import org.jetbrains.annotations.NotNull;import java.util.Arrays;
import java.util.List;public class SSIssueRegistry extends IssueRegistry {@NotNull@Overridepublic List<Issue> getIssues() {return Arrays.asList(SSLogDetector.sISSUE);}
}

继承Detector类并实现Scanner接口:

package com.brett.lintcheck;import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.ClassContext;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;import java.util.Arrays;
import java.util.List;public class SSLogDetector extends Detector implements Detector.ClassScanner {public static final Issue sISSUE = Issue.create("LogUse","You use android.util.Log not LogUtils","Logging should be avoided in production for security and performance reasons." +"Therefore, we created a LogUtils that wraps all our calls to Logger and disable them for release flavor.", Category.MESSAGES,//category6,//must be[1,10]Severity.ERROR,//severity of the issuenew Implementation(SSLogDetector.class, Scope.CLASS_FILE_SCOPE));@Nullable@Overridepublic List<String> getApplicableCallNames() {return Arrays.asList("v","d","i","w","e","wtf");}@Nullable@Overridepublic List<String> getApplicableMethodNames() {return Arrays.asList("v","d","i","w","e","wtf");}@Overridepublic void checkCall(@NotNull ClassContext context, @NotNull ClassNode classNode, @NotNull MethodNode method, @NotNull MethodInsnNode call) {String owner = call.owner;if(owner.startsWith("android/util/Log")){context.report(sISSUE,method,call,context.getLocation(call),"You must be our 'LogUtils' instend of android.util.Log");}}
}

完成上面三步便可以自定义Lint。
接着,我们在任何模块种引入该依赖库,然后调用Log相关的方法,接着在终端执行如下命令:

gradlew lint

接着终端就会出现如下log:
在这里插入图片描述

  1. 自定义Detector可以实现一个或多个Scanner接口,选择实现哪种接口取决于你想要的扫描范围。
    Lint API 中内置了很多 Scanner:
Scanner 类型Desc
UastScanner扫描 Java、Kotlin 源文件
XmlScanner扫描 XML 文件
ResourceFolderScanner扫描资源文件夹
ClassScanner扫描 Class 文件
BinaryResourceScanner扫描二进制资源文件
GradleScanner扫描Gradle脚本,借此可以检测组件之间的依赖关系
JavaScanner扫描Java文件
OtherFileScanner其他文件
  1. Lint扫描顺序(Detector调用顺序):Manifest file->Resource files(按照资源目录字母顺序)->Java sources->Java classes->Gradle files->Generic files(其他所有文件)->Proguard files->Property files。
  2. Issue。用于Detector发现并输出Bug。各个参数含义如下:
 public static final Issue ISSUE = Issue.create("LogId", //第一无二的id即可 "不要直接使用Log", //描述信息"不要直接使用Log",     // 描述信息Category.MESSAGES,5,//优先级[1-10]Severity.WARNING,new Implementation(LogDetector.class, Scope.JAVA_FILE_SCOPE)//文件类型意味着只扫描java文件);

官方文档

https://developer.android.google.cn/studio/write/lint?hl=zh_cn

参考资料

https://www.jianshu.com/p/ae906ed4b7db


http://chatgpt.dhexx.cn/article/wxEoRSJr.shtml

相关文章

代码审查的必要性和最佳实践

目录 代码审查的流程 代码审查的争议 加班要累死了&#xff0c;完成项目都来不及&#xff0c;还做什么代码审查&#xff1f; 代码审查太费时间&#xff0c;改来改去无非是一些格式、注释、命名之类不痛不痒的问题。 团队的习惯和流程就是不做代码审查&#xff0c;大家都是…

如何审查网页元素

如何审查网页元素 对于一个优秀的爬虫工程师而言&#xff0c;要善于发现网页元素的规律&#xff0c;并且能从中提炼出有效的信息。因此&#xff0c;在动手编写爬虫程序前&#xff0c;必须要对网页元素进行审查。本节将讲解如何使用“浏览器”审查网页元素。 浏览器都自带检查…

代码审查(文档整理)

常用的代码审查工具 git/SUBVERSIONGerritUpsource显示代码变更√√√使用源码仓库√√在线代码讨论√√异步审查支持√√使用协议GPLv2/Apache License 2.0Apache License 2.0付费license 代码审查 范根检查法 轻量级的审查流程 结对编程同步代码审查异步代码审查 代码审查需…

代码审查“查”什么?

让我们来谈谈代码审查&#xff08;Code Review&#xff09;。如果花几秒钟去搜索有关内容&#xff0c;你会发现许多论述代码审查好处的文章&#xff08;例如&#xff0c;Jeff Atwood的这篇文章&#xff09;。你还会发现许多介绍如何使用代码审查工具的文档&#xff0c;比如我们…

uniapp的uview2.0框架u--textarea组件无法换行,换行无效问题解决方案

问题描述 在使用uniapp的uview2.0框架u–textarea组件时&#xff0c;想要使u–textarea支持换行输入&#xff0c;但是默认不支持换行输入&#xff0c;各种百度&#xff0c;没有找到解决问题的方案&#xff0c;最后只有查看源码如下 但发现源码没有对属性有过多的处理&#xff…

微信小程序textarea问题总结

微信小程序textarea问题总结 1.textarea多行输入框。该组件是原生组件 2.参考文档中的原生组件说明https://developers.weixin.qq.com/miniprogram/dev/component/native-component.html 原生组件的层级是最高的&#xff0c;所以页面中的其他组件无论设置 z-index 为多少&am…

JavaFX设置TextArea文本内容

本篇主要内容为设置TextArea的内容&#xff0c;涉及的知识面包括如何添加视图和控制层的关联&#xff0c;调用TextArea的方法设置文本内容&#xff0c;不仅实用于TextArea&#xff0c;还可以设置其它如Button&#xff0c;Label&#xff0c;TextField等组件的文本域或者其它属性…

原生div实现textarea

文章导航 为什么要用div实现textarea关键词实现效果源码示例 为什么要用div实现textarea div 实现 textarea 可以实现高度自适应 关键词 contenteditable: true|false 可以将元素内容区域调整为可编辑 实现效果 源码示例 &#x1f383; index.html <!DOCTYPE html>…

html textarea设置只读属性吗,HTML

HTML中的只读属性用于指定textarea元素为只读。如果文本区域为只读&#xff0c;则其内容无法更改&#xff0c;但可以复制并突出显示。这是一个布尔属性。 用法: Contents... 范例1&#xff1a;本示例使用只读属性在仅可读的输入textarea上写入内容。 HTML Textarea readonly A…

让textarea 只读

网上找到几种方案 都试了下 editable"false" readonly"readonly" disabled"disabled" 不知道是不是语法我写错了 但这么写效果是 &#xff08;顺寻代码和图片一致&#xff09; 第二个和第三个都是不能点 不能修改 第一个可以点可以修改

html页面只读,textarea只读 readonly =true;

Java TextArea 只读问题 TextArea ta new TextArea("",10,15,TextArea.SCROLLBARS_VEICAL_ON报啥错啊。 textarea怎么设置为只读 readonly true; C#语言&#xff1a;Listview控件中加了一个textarea&#xff0c;怎么在前台页面将textarea转化服务器控件&#xff0c;…

如何让textarea不可编辑只可读(博客常用)

如何让textarea不可编辑只可读&#xff08;博客常用&#xff09;。 Textarea的相关属性解释&#xff1a; rows&#xff1a;行高&#xff0c;就是你这个文本框能显示多少行文字 cols&#xff1a;宽度&#xff0c;代表你这个文本框有多宽 readonly&#xff1a;有个值readonly&am…

[评价体系] 1、数据规范化/无量纲化方法

目录 1 数据格式 2 不同属性类型的指标规范化方法 2.1 效益型指标&#xff1a;即该指标越大越好 2.2 成本型指标&#xff1a;即该指标越小越好 2.3 固定型/中间型指标&#xff1a;在某个固定值处最好 2.4 区间型指标&#xff1a;有最佳区间 2.5 偏离区间型&#xff1a;偏…

机器学习学习笔记(3)——量纲与无量纲,标准化、归一化、正则化

量纲、无量纲&#xff0c;标准化、归一化、正则化是我百度了很多次都不进脑子的知识&#xff0c;所以我决定还是放在博客上面。 不过鉴于我查阅了很多资料&#xff0c;说是有许多的坑&#xff0c;所以我也不清楚我的理解和解释是否是坑&#xff0c;具体的就留给各位来帮忙评判了…

数据的无量纲化处理和标准化处理的区别是什么

数据的无量纲化处理和标准化处理的区别是什么 请教:两者除了方法上有所不同外,在其他方面还有什么区别? 解答: 标准化处理方法是无量纲化处理的一种方法。除此之外,还有相对化处理方法(包括初值比处理)、函数化(功效系数)方法,等等。由于标准化处理方法可以与分布…

数据归归一化方法(标准化)

数据归一化方法 数据标准化&#xff08;normalization&#xff09;数据标准化处理主要包括数据同趋化处理和无量纲化处理两个方面。 数据同趋化处理主要解决不同性质数据问题&#xff0c;对不同性质指标直接加总不能正确反映不同作用力的综合结果&#xff0c;须先考虑改变逆指标…

特征锦囊:特征无量纲化的常见操作方法

今日锦囊 特征无量纲化的常见操作方法 第一招&#xff0c;从简单的特征量纲处理开始&#xff0c;这里介绍了3种无量纲化操作的方法&#xff0c;同时也附上相关的包以及调用方法&#xff0c;欢迎补充&#xff01; 无量纲化&#xff1a;即nondimensionalize 或者dimensionless&am…

数据挖掘——无量纲化

在进行特征选择之前&#xff0c;一般会先进行数据无量纲化处理&#xff0c;这样&#xff0c;表征不同属性&#xff08;单位不同&#xff09;的各特征之间才有可比性&#xff0c;如1cm 与 0.1kg 你怎么比&#xff1f;无量纲处理方法很多&#xff0c;使用不同的方法&#xff0c;对…

量纲与无量纲、标准化、归一化、正则化【能够帮助梯度下降中学习进度收敛的更快、提升模型的收敛速度提升模型的精度、防止模型过拟合,提高模型的泛化能力】

目录 1 量纲与无量纲 1.1 量纲 1.2 无量纲 2 标准化 3 归一化 归一化的好处 4 正则化 5 总结 1 量纲与无量纲 1.1 量纲 物理量的大小与单位有关。就比如1块钱和1分钱&#xff0c;就是两个不同的量纲&#xff0c;因为度量的单位不同了。 1.2 无量纲 物理量大小与单位…

数据预处理----数据无量纲化(归一化和标准化)

文章目录 概述数据挖掘的五大步骤skleran中的数据处理和特征工程 数据预处理Preprocessing数据无量纲化数据归一化---MinMaxScaler示例 数据标准化----StandardScaler示例 MinMaxScaler和StandardScaler应该选哪个 概述 数据挖掘的五大步骤 数据获取数据预处理 数据预处理是从…