草庐IT

聊聊我所知道的 Android 相关的代码检测

无敌恶霸 2023-03-28 原文

聊聊我所知道的 Android 相关的代码检测

因为笔者业务开发能力不太够,所以会经常做一些周边的杂活。曾经做过在团队里建立代码检测机制的工作,所以我想简单地说下这部分的内容。

代码检测的地位

首先要说的是 软件开发的检测 的目的是什么,以及都有那些类型的检测。

在我看来,检测的目的就是为了 保证代码可以满足某些要求

以笔者浅显的见识,现在觉得一般的软件开发检测包括:

  • 代码检测
  • 业务功能的单元测试
  • 模块整体(比如一个 aar / 一个 apk)的自动化测试
  • 真人 QA 介入的测试

其中从上到下,

  • 测试执行成本(设计成本、人工成本等)越来越大;
  • 测试执行所用时间越来越长;
  • 测试执行频率越来越低;
  • 发现问题以后修复的成本越来越高;

今天只讨论其中 代码检测 这个环节,其余的环节因为笔者了解的并不多,就不在本次献丑了。

代码检测指的是什么?

笔者认为 代码检测 指的是开发者从 提出代码修改初步确认代码没有简单的错误 的这个过程的。因为将 业务功能 相关的单元测试划分出去了,所以这部分 代码检测 是不包含业务功能的检测的。也就是说,这部分只进行

  1. 静态代码的格式检测

  2. 静态代码的健壮性的检测

这样子说可能比较模糊,我举几个现在比较常见的检测规则:

  • 改动是否包含魔数
  • 改动里的变量名是否符合项目的要求(比如要求静态变量使用大写命名,属性变量使用驼峰格式命名)
  • 改动是否导致文件过于庞大(比如说单个文件超过了 1200 行,很难让人读懂)
  • 改动是否导致类继承层级过多(比如改完了以后当前类到 Activity 有五层继承关系)
  • 改动是否导致某个方法过于复杂(比如单个方法行数超过了 150 行,某个方法里的 if / for / when 过多等)
  • ……

这些检测,在一些手熟的开发者手中,是很难出现的,因为这些人大都会很注意程序的基本设计,会有意识避免这些会导致代码可读性变差的写法。不过对于一些项目比较着急、或者像笔者这样不太注意的开发者来说,就很容易写出这种代码。

这种代码其实在 Code Review 环节是很容易发现的,不过如果单纯依靠人来做这样的检测的话,一是比较慢,二是比较累。所以最好的方法就是增加自动检测的机制,如果不符合这些条件的话,可以直接提示出来进行改动。这样,通过检测的代码就有了一个最基本的质量底线。这也就是上面提到的 代码检测 了。从这个角度说,代码检测 其实是用来 1)规范代码基本写法 2)帮助 Code Review 的。

Android 上如何进行代码检测

对于开发者来说,开发上面这种检测插件无疑是很简单的事情。但是对于笔者来说,开发这些东西需要查看大量语法解析的资料,所以还是采用已经有的开源方案了。

对于 Android 上要进行的代码检测,有这几种可选的方案:

  • 纯静态检测文本的方式解析代码
  • 解析代码关系,检测代码

接下来分别介绍

纯静态检测代码

这种检测方式,类似于 python 这类“解析型语言”。特点是只需要输入一个 java 文件,不需要它所依赖的类的文件(比如说父类的类文件),即可以完成检测。甚至不需要整个文件,只输入其中的一个方法也可以进行检测!因为其只依赖文本内容的特征,所以笔者称之为静态检测代码。

这部分已有比较成熟的开源方案。其中对于 java 有大名鼎鼎的 check-style; 对于 kotlin 则有新秀检测方案 detekt 。两者的接入方式比较一样,使用方式也很类似。

限于篇幅,具体的用法就不在此转发了,以官网的为准,各位朋友可以查看官网的使用说明。

虽然要对付的语言不一样,但是 check-styledetekt 如此相似,让人不由想看看其内部实现的方式。简单地查看了各自的源码后,会发现其实内部的原理都一样,只是表现的形式不同罢了。

两者的思路其实大概流程都是下面这种流程(就不细说了,因为太详细的话,笔者也不是很明白了;而且这里的描述可能不够清楚,如果看不明白的话,可能是因为笔者的理解不对或者是表达能力有限,可以先不关注。)

  1. 基于输入的文件内容解析出来一个语法树。
  2. 会有多个写好的检测规则类,这些检测规则其实就是 java语法树遍历器 / kotlin 语法树遍历器。
    1. 距离:比如说检查文件
  3. 在生成的语法树上,遍历每一个元素:
    1. 对每一个元素,都遍历每一个规则。
  4. 上述的遍历完成后,即相当于每一个规则都遍历了该语法树,也即相当于对输入的文件进行了一遍遍历。各个规则也都完成了对文件的检测,采集到了文件不通过检测的部分。
  5. 最后将所有不通过检测的部分全都输出,也就完成了文本的检测了。

解析代码关系,检测代码

相比较于上面的类似 “解释型语言” ,这种检测方式更像 “编译型语言” 。这种检测方式只依靠单一的输入文件是无法完成的,需要输入的是一个可以作为整体进行“链接”的项目。比如 Android Studio 自带的 lint 就是这类检测方式。

举个例子说明:

  • 假设需要禁止一个类的子类太过庞大(比如继承深度不能超过 5,每一代的继承自雷数量不能超过 6)。

这类涉及多个文件的检测,明显不是纯文本可以完成的,需要将其他的文件一并读取分析才可以完成判断。

这种检测因为更加复杂,运行一次的用时也会更多,差不多类似于进行一次项目的编译了。

但是因为笔者没有实际运用过 lint 进行复杂的代码检测,所以在这里不多谈了。

总结

代码检测是用来规范代码格式的,对于保证代码的最低质量还是比较有用的。当然了如果为了写出更好的代码,还是需要多多思考,少复制粘贴的。

有关聊聊我所知道的 Android 相关的代码检测的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  3. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  4. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  5. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  6. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  7. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  8. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  9. 程序员如何提高代码能力? - 2

    前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源

  10. 7个大一C语言必学的程序 / C语言经典代码大全 - 2

    嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来

随机推荐