Android Lint规则(包括翻译规则)和空指针检查研究

  • Post author:
  • Post category:其他



一、Android Lint


  • Lint简介

Android Lint 是有 Android SDK 提供的一种静态代码检测工具,用于检测 Android 的代码质量。Android Lint 的源码集成在 Android SDK Tools 16 及更高的版本中,我们可以在项目目录下通过 ./gradlew lint 命令调用,也可以通过 Android Studio 的 【Analyze】->【Inspect Code】路径调用 Lint 检查。

Lint 是 Android 提供的一个强大的,用于静态扫描应用源码并找出其中的潜在问题的实用工具。lint 工具可以检查你的 Android 项目源文件是否有潜在的错误,以及在正确性、安全性、性能、易用性、无障碍性和国际化方面是否需要优化改进。

Lint 既可以用作命令行工具,也可以与 Eclipse 和 IntelliJ 集成在一起。它被设计成独立于 IDE 的工具,我们可以在 Android Studio 中非常方便的使用它。


  • Lint的工作流


  • Lint的问题等级

Fatal严重致命的、Error错误、Warning警告、Information提示、Ignore忽略 不提示


实际测试Fatal、Error只是错误代码部分增加红色和下划线提示,不会报错和阻断编译。


  • Lint规则使用方法

1、配置Gralde,build.gradle 文件的android下增加lintOptions,下面列举 lintOptions 可定义的选项

android {
    lintOptions {
        // true--关闭lint报告的分析进度
        quiet true
        // true--错误发生后停止gradle构建
        abortOnError false
        // true--只报告error
        ignoreWarnings true
        // true--忽略有错误的文件的全/绝对路径(默认是true)
        //absolutePaths true
        // true--检查所有问题点,包含其他默认关闭项
        checkAllWarnings true
        // true--所有warning当做error
        warningsAsErrors true
        // 关闭指定问题检查
        disable 'TypographyFractions', 'TypographyQuotes'
        // 打开指定问题检查
        enable 'RtlHardcoded', 'RtlCompat', 'RtlEnabled'
        // 仅检查指定问题
        check 'NewApi', 'InlinedApi'
        // true--error输出文件不包含源码行号
        noLines true
        // true--显示错误的所有发生位置,不截取
        showAll true
        // 回退lint设置(默认规则)
        lintConfig file("default-lint.xml")
        // true--生成txt格式报告(默认false)
        textReport true
        // 重定向输出;可以是文件或'stdout'
        textOutput 'stdout'
        // true--生成XML格式报告
        xmlReport false
        // 指定xml报告文档(默认lint-results.xml)
        xmlOutput file("lint-report.xml")
        // true--生成HTML报告(带问题解释,源码位置,等)
        htmlReport true
        // html报告可选路径(构建器默认是lint-results.html )
        htmlOutput file("lint-report.html")
        //  true--所有正式版构建执行规则生成崩溃的lint检查,如果有崩溃问题将停止构建
        checkReleaseBuilds true
        // 在发布版本编译时检查(即使不包含lint目标),指定问题的规则生成崩溃
        fatal 'NewApi', 'InlineApi'
        // 指定问题的规则生成错误
        error 'Wakelock', 'TextViewEdits'
        // 指定问题的规则生成警告
        warning 'ResourceAsColor'
        // 忽略指定问题的规则(同关闭检查)
        ignore 'TypographyQuotes'
    }
}

2、指定规则

// 回退lint设置(默认规则)

lintConfig file(“default-lint.xml”)

default-lint.xml手动创建放在当前module目录

类似



这种可以单独写在xml中

3、执行Lint检测

分为三种:

第一种,常见的实时检测,AndroidStudio自带的,可以通过lintOption配置


显示


错误的等级。

测试 我把NewApi和InlineApi这种错误改成Fatal致命的,build同步一下

看代码

只会提示但不会阻挡编译和运行,Error的效果相同。

Warning和Infomational的效果一样,也是只会提示但不会阻挡编译和运行。

忽略检测

指定方法忽略

lint在实时检测只能发挥提示的作用,进一步查看lint规则,找到需要的规则。

下方有专门的规则列表



https://wiki.h3d.com.cn/pages/viewpage.action?pageId=86346982


针对空指针,手动写个报错代码


未赋值实时检测lint是有提示的。


手动赋值为null竟然不显示,下面会依次查找现有规则。

第二种,手动静态扫描

第三种,Gradle命令

  1. ./gradlew lint
  2. ./gradlew app:lintDebug
  3. ./gradlew app:lintTiktokI18nDebug

这些命令,会执行gradle文件,应用lintOptions的配置,进行Lint检测,结果会放在配置中指定的xml或html中。

生成html报告中没有找到空指针相关的提示。


  • Lint规则


Lint规则


官方规则



Android Lint Checks – Android Studio Project Site


我翻译后的规则单独都放在了这里



https://wiki.h3d.com.cn/pages/viewpage.action?pageId=86346982

lint规则包含一下几个分类


查找android官方lint库,没有找到类似空指针这种语法级别的规则。


二、


Lint自定义规则


1、为什么需要自定义 Lint ?


由于每个业务线自身的需求,Lint 默认的检查项目可能不能满足我们的需求。 比如司机端一个自定义控件需要抽成一个库给其他项目使用,但是我们希望使用者必须在 XML 中定义一个属性,否则组件无法正常运行,我们希望Lint能够对此进行检查,并在忘记添加此属性时给出明确的错误提示。

再比如,我们的基础组件有一个日志库,能够方便的在 release 版本中关闭日志输出,还能够把日志输出到指定的文件中方便事后分析,这时如果来了一个新同学,他可能还是习惯性的用 android.util.Log 来打印日志,我们希望能够检测到本项目中所有使用了 android.util.Log 的代码,并发出警告。 要满足这些自定义需求,我们就需要通过 Android Lint 的扩展机制自己定制 Lint 规则。

同时发现潜在的空指针未赋值等造成Crash的语法问题。


2、lint Api

如果要查看 lint 工具支持的 issue 的完整列表和它们所对应的 issue ID,可以使用 lint –list 命令。

Lint 中包括多种类型的 Scanner 如下,其中最常用的是扫描 Java 源文件和 XML 文件的 Scanner:

SourceCodeScanner:扫描 Java 和kotlin源文件

XmlScanner:扫描 XML 文件

ClassScanner:扫描 class 文件

BinaryResourceScanner:扫描二进制资源文件

ResourceFolderScanner:扫描资源文件夹

GradleScanner:扫描 Gradle 脚本

OtherFileScanner:扫描其他类型文件

使用Detecotor探测器,实现多个scanner扫描,然后找出lint问题,并通过Issue提示出来。

最后通过注册器IssueRegistry,注册到androidLint。


3、自定义lint

3.1、建立lint扫描的module(lint_tools)

类型选Java or Kotlin library,命名lint_tools

包名com.h3d.qqx5.lint,保持现有结构

  • build.gradle设置导包

导包

compileOnly “com.android.tools.lint:lint-api:$lint_version”

compileOnly “com.android.tools.lint:lint-checks:$lint_version”

一定要是compileOnly确保只编译不打包进主Module。

我这里使用kotlin的module所以额外有kotlin导包。

  • 编写一个代码检查器LogDetector.kt

继承Detector探测器,实现SourceCodeScanner接口,该接口支持扫描java和kotlin类。

看看源码该扫描类的方法

返回此检测器感兴趣的方法名称列表,或者空,也就是该类调用了什么方法,需要设置要检测的方法名,限制了范围。

方法访问,由getApplicableMethodNames()限制的方法名,该方法就可以检测到指定的方法。

比如检查App内调用了系统Log和System.out.println方法的标记并提示报错。

isMemberInClass是检查指定的方法的类名是否存在于该检测类中。存在我们就荣国ISSUE来跟Lint一样的样式提示出来。

参数分别是1、取消检测使用的注解名称 2和3、lint提示 4、Lint类型(性能、Icon、国际化这种,默认Lint)5、优先级1-10 6、报错等级 7、默认实现

编写好的ISSUE需要注册到Lint

  • 编写注册类

  • 编写module的build.gradle,全局配置文件设置注册类

3.2、主module使用lint_tools

由于安卓官方推荐的方式是把lint_tools打成jar,然后命名为lint.jar直接放到开发者C盘的

.android/lint

目录下使用,这将导致所有工程都要使用该lint,没必要。

lint_tools是lib工程只会生成jar,如果将该lint_tools直接导入主module,因为module是以jar的形式,无法直接发布,需要以aar形式引入。

这就需要中间module作为aar,创建Android Module (aar) lint_check

build.gradle下发布lint_tools module

其他选项和主module保持一致

主Module导入

gradle同步一下,以后build或者编译或者lint运行都可以实时检测。

但是空指针怎么判断还没有具体方法,继续研究~

改造LogDetetor,设想检测方法,所有get开头的方法判断获取到的内容是否为空

但是发现并没有错误提示。

后来查看api注释,只有添加的调用方法名,才能被Lint检查,Log之所以可以是因为它是系统Api,被广泛调用。也就是说已经被调用的通用代码可以使用,但是空指针一般都是未知的方法。

尝试加入

必须明文传入的方法才可以被检测,所以空指针的Lint暂时未找到实现方式。


  • Java代码增加空安全语法包裹

设计思路:传入原始数据,返回不为空的数据

类型必须要使用到泛型,因为Java的泛型实例化也是必须使用class类型的,由于data如果为空是不可能拿到class的,所以还需要额外的class传入,泛型这里是class<T>

对一个可能的数据空,调用该方法,除非实例化异常,否则很难出空指针异常了。


重要代码优先转Kotlin



版权声明:本文为cplasf2012原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。