专栏首页逮虾户再谈Android Lint

再谈Android Lint

先把上一篇自定义lint的文章链接放在开头

自定义lintDemo项目

存粹个人看法哦,静态扫描我觉得是一个在开发过程中就去避免掉一部分bug的重要的工具。但是对这方面的介绍的文章还是有点少,我其实写的也不怎么样,但是起码集思广益,互相提高吧。

我之前写的Lint的文章,只从实现层之类的去描述了下如何自定义一个lint扫描规则,但是也没有说清楚什么lint到底是基于什么去写的,这边进一步进行一次补充。

AST(Abstract Syntax Tree)抽象语法树

抽象语法树(abstract syntax code,AST)是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,这所以说是抽象的,是因为抽象语法树并不会表示出真实语法出现的每一个细节,比如说,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现。抽象语法树并不依赖于源语言的语法,也就是说语法分析阶段所采用的上下文无文文法,因为在写文法时,经常会对文法进行等价的转换(消除左递归,回溯,二义性等),这样会给文法分析引入一些多余的成分,对后续阶段造成不利影响,甚至会使合个阶段变得混乱。因些,很多编译器经常要独立地构造语法分析树,为前端,后端建立一个清晰的接口。

我们简单的从这张图来看下java的AST的过程。

步骤一:词法分析,将源代码的字符流转变为 Token 列表。

一个个读取源代码,按照预定规则合并成 Token,Token 是编译过程的最小元素,关键字、变量名、字面量、运算符等都可以成为 Token。

步骤二:语法分析,根据 Token 流来构造树形表达式也就是 AST。

语法树的每一个节点都代表着程序代码中的一个语法结构,如类型、修饰符、运算符等。经过这个步骤后,编译器就基本不会再对源码文件进行操作了,后续的操作都建立在抽象语法树之上。

apt的过程就是在源代码被转化成ast之后执行的对注解的一次process,所以我们能在apt的过程中获取到所有注解类以及注解的类信息相关。

java同学们熟悉的lombok库,就是基于ast语法树的一个修改,详细的可以参考下这篇文章的修改方式。

    public boolean isSubType(Element element, String className) {
        return element != null && isSubType(element.asType(), className);
    }
复制代码

其实我最近就一直有一种奇怪的感觉,为什么Apt的element和lint的感觉非常的相似,哈哈哈。就像上面这种apt的时候的类型判断代码一样。

Android Lint

而对于Android Lint来说,它本质上就是AST抽象语法树,通过语法树获取到所有代码的节点,之后对其进行自定义的逻辑判断,举个例子,当前类是不是符合了特定标准,比如是不是一个构造器,是不是一个方法,方法名是什么之类的,当符合特定规则之后就会抛出一个Issue。

在Android Lint迭代过程中,扫描源代码的Scanner先后经历了三个版本的AST。

  1. 最开始使用的是JavaScanner,Lint通过Lombok库将Java源码解析成AST(抽象语法树),然后由JavaScanner扫描。
  2. 在Android Studio 2.2和lint-api 25.2.0版本中,Lint工具将Lombok AST替换为PSI,同时弃用JavaScanner,推荐使用JavaPsiScanner。 PSI是JetBrains在IDEA中解析Java源码生成语法树后提供的API。相比之前的Lombok AST,可以支持Java 1.8、类型解析等。使用JavaPsiScanner实现的自定义Lint规则,可以被加载到Android Studio 2.2+版本中,在编写Android代码时实时执行。
  3. 在Android Studio 3.0和lint-api 25.4.0版本中,Lint工具将PSI替换为UAST,同时推荐使用新的UastScanner。

UAST

UAST是JetBrains在IDEA新版本中用于替换PSI的API。UAST更加语言无关,除了支持Java,还可以支持Kotlin。

UAST is short for "Universal AST" and is an abstract syntax tree library which abstracts away details about Java versus Kotlin versus other similar languages and lets the client of the library access the AST in a unified way.

UAST isn't actually a full replacement for PSI; it augments PSI. Essentially, UAST is used for the inside of methods (e.g. method bodies), and things like field initializers. PSI continues to be used at the outer level: for packages, classes, and methods (declarations and signatures). There are also wrappers around some of these for convenience.

我仔细阅读了下官方对于uast的定义,首先正如开篇所说,UAST是一个更普遍的AST,其适用范围不仅仅局限于java代码,同时还能支持kotlin以及起来相似语言。

但是PSI也并不完全就是已经被UAST所取代的趋势,还是可以拿来做一些别的简单的java扫描工作的。

在不熟悉API的情况下如何更好的写一个Lint呢?

以我个人的开发经验来看,我会从Android原生提供的Lint规则中去寻找可能适合我的逻辑。举个例子,我之前在使用埋点的时候我不小心给字符串前面加了个空格,我这个时候就会反思,是不是可以通过静态扫描的方式去搞,但是这个时候api不熟悉怎么办呢??

谁家代码不是抄呀,哈哈哈。其实我之前在用TextView的时候发现当我直接设置一个字符串进去的时候lint会爆黄。有思路就可以抄代码,我去找到了SetTextDetector,然后我就根据其中的代码,完成了这个静态扫描工具的开发。

public class EventSpaceDetector extends Detector implements Detector.UastScanner {

    static final Issue ISSUE = Issue.create(
            "event_space_issue",    //唯一 ID
            "埋点不允许出现空格",    //简单描述
            "你不知道有时候卵用空格会出问题的吗",  //详细描述
            Category.CORRECTNESS,   //问题种类(正确性、安全性等)
            6,  //权重
            Severity.WARNING,   //问题严重程度(忽略、警告、错误)
            new Implementation(     //实现,包括处理实例和作用域
                    EventSpaceDetector.class,
                    Scope.JAVA_FILE_SCOPE));
    private final String packageName = "com.kronos.sample";


    @Override
    public List> getApplicableUastTypes() {
        List> types = new ArrayList<>();
        types.add(UCallExpression.class);
        return types;
    }

    @Override
    public UElementHandler createUastHandler(@NotNull JavaContext context) {
        return new UElementHandler() {

            @Override
            public void visitCallExpression(@NotNull UCallExpression node) {
                checkIsConstructorCall(node);
            }

            private void checkIsConstructorCall(UCallExpression node) {
                if (!UastExpressionUtils.isConstructorCall(node)) {
                    return;
                }
                UReferenceExpression classRef = node.getClassReference();
                if (classRef != null) {
                    String className = UastUtils.getQualifiedName(classRef);
                    String value = packageName + ".Event";
                    List args = node.getValueArguments();
                    for (UExpression element : args) {
                        if (element instanceof ULiteralExpression) {
                            Object stringValue = ((ULiteralExpression) element).getValue();
                            if (stringValue instanceof String && stringValue.toString().contains(" ")) {
                                if (!TextUtils.isEmpty(value) && className.equals(value)) {
                                    context.report(ISSUE, node, context.getLocation(node),
                                            "谁给你的胆子用空格的");
                                }
                            }
                        }
                        element.getExpressionType();
                    }

                }
            }
        };
    }

}
复制代码

原谅我的粗鄙啊,这个文本用的是过分了点。但是实际上我在SetTextDetector中找到了ULiteralExpression,这个就是当前的语法树中的变量值,我将它的value取出来之后,判断了下内容是否含有空格,如果有则在当前地方直接抛出一个issue。这样我就能让项目内所有给埋点的代码加了空格的做一次提醒,起码可以避免掉一部分开发的时候的粗心大意。

总结

我个人看法UAST的资料网上真实的是不多的,所以开发如果要想写成特别复杂的这种扫描规则就必须要靠当前系统给我们提供的那些已经定义好的lint,然后去其中分析他们是如何写的,这样就可以写出你自己想要的自定义lint了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Custom Lint in Action

    Android Lint是Google提供的静态代码检查工具,使用Lint可以对Android项目源码进行扫描和检查,发现代码潜在的问题,或者辅助开发者统一编码...

    宅男潇涧
  • 静态测试技术之 Lint 冗余资源清理

    谈到冗余资源清理,我们不妨先来看看Android的资源组织方式和访问方式。本文通过冗余资源的清理 、冗余资源清理原理解析以及手管的冗余资源清理应用三方面来讲解分...

    腾讯移动品质中心TMQ
  • 美团外卖Android Lint代码检查实践

    美团技术团队
  • 管中窥豹:结合NewApi实践来了解Lint代码扫描

    导读 lint是著名的C/C++语言静态代码分析工具之一,Android Lint顾名思义,针对Android的静态代码分析工具,能够对Android项目中潜在...

    腾讯移动品质中心TMQ
  • 代码洁癖症的我,学习Lint学到心态爆炸

    以前对下面的问题,我的态度是,不报错就是没问题,报错就用快捷键,根据Android Studio提示修复问题,从来不去问个为什么?现在代码洁癖症越来越严重的我,...

    Rouse
  • AndroidLintWatchDog Custom-Lint 自定义Lint检查的实现

    Android自定义Lint检查有效提升代码质量、避免人工的低级失误、规范代码,属于程序自动化的内容,这部分内容涉及的资料较少,但是实际意义重大...

    open
  • Android 性能优化:使用 Lint 优化代码、去除多余资源

    *本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 前言 在保证代码没有功能问题,完成业务开发之余,有追求的程序员还要追求代码的规范、可维护性...

    张拭心 shixinzhang
  • 2019年底腾讯Android研发岗面试复盘总结,还是体系的学习效果高

    很多打算年初跳槽的,因为疫情的原因放缓了节奏,在家办公,不像平时那么方便去找工作跑面试了。

    Android技术干货分享
  • Android 项目代码质量保证实践

    一个项目开发必然会涉及团队协作,而工程质量就需要团队去保证。一般我们期望的代码:无潜在风险、无重复逻辑、风格无差异、可阅读性好、新人上手速度快等。为了达到上述目...

    小老鼠
  • Builtin Lint Detectors (1)

    本文主要介绍的是Lint工具中自带的与Android开发相关的lint检查项,通过查看lint检查项的描述及其代码实现,我发现这里面存在不少应用开发编码的Bes...

    宅男潇涧
  • Android代码分析工具lint学习

    1 lint简介 1.1 概述 lint是随Android SDK自带的一个静态代码分析工具。它用来对Android工程的源文件进行检查,找出在正确性、安全、性...

    用户1172465
  • Android Studio 3.2新功能特性

    我就是马云飞
  • 使用Android Lint检查代码缺陷

    Android Lint 是Android自带的代码检查工具,它能帮助我们识别很多潜在的错误。

    zhangyunfeiVir
  • Kotlin代码检查在美团的探索与实践

    Kotlin有着诸多的特性,比如空指针安全、方法扩展、支持函数式编程、丰富的语法糖等。这些特性使得Kotlin的代码比Java简洁优雅许多,提高了代码的可读性和...

    美团技术团队
  • android 自定义Lint

    概述 Android Lint是Google提供给Android开发者的静态代码检查工具。使用Lint对Android工程代码进行扫描和检查,可以发现代码潜在的...

    xiangzhihong
  • Now in Android #15 —— 最新 Android 知识分享

    https://medium.com/androiddevelopers/now-in-android-15-44bf3307a8f2

    路遥TM
  • 【技术博客】Android自定义Lint实践

    概述 Android Lint是Google提供给Android开发者的静态代码检查工具。使用Lint对Android工程代码进行扫描和检查,可以发现代码潜在的...

    美团技术团队
  • Lint Tool Analysis (2)

    本系列的几篇源码分析文档意义不大,如果你正好也在研究lint源码,或者你想知道前面自定义lint规则中提出的那几个问题,抑或你只是想大致了解下lint的源码都有...

    宅男潇涧
  • 《Android编程权威指南》之Android应用的调试篇

    处理应用的bug,这是每个程序员的基本功,实际项目中天天都有各式各样的bug,因此学会如何使用Logcat、Android Lint以及Android Stud...

    用户8928967

扫码关注云+社区

领取腾讯云代金券