专栏首页Open软件开发小组AndroidLintWatchDog Custom-Lint 自定义Lint检查的实现

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

前言

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

提示:文中链接需要点击文章末尾处阅读原文才能点击。

作者 介绍

QJoy

目前供职于优酷来疯团队,除了android相关的日常业务工作,平时个人喜爱研究自动部署测试,音视频相关,以及基于OpenGL的特效研究。热衷开源、分享交流知识。

一、 Coding时遇到的问题

1

案例

案例1:com.alibaba.fastjson.JSON

工程中经常会使用 FastJson 来解析 Json 数据,由于会使用反射机制构造 JavaBean 对象,那么 release 版本混淆的情况下,如果没有对相应的 JavaBean 对象做keep处理的话,JavaBean 无法成功构建,从而出现对象为 NULL 的情况。往往会在临上线的两三天在release包中突然发现莫名的崩溃、功能失效之类的问题,都是由于这个原因。造成每每发版本就要加班的窘境。

案例2:activity基类

由于有一些统计,例如友盟统计活跃的需求,需要在 Activity 中的 OnResume/OnPause 实现某些方法,当然还有很多我们项目自身的原因,需要所有工程中的 Activity都要继承自某一个我们实现的 BaseActivity,如果是个小工程小团队,我们可以全局搜索一下,定期检查一下,都可以。如果是个十余人甚至更大的团队,每个版本的需求中都有可能产生新的 Activity,或者在大的工程重构后,能否保证人不会犯错,不会忘记将我们的 Activity 继承自我们的 BaseActivity ?

案例3:团队的编码规范

当一个团队技术负责人认认真真的制订了少量有效的编码规范后,苦口婆心的像是传销似的要求团队成员遵循,难道需要我们对工程中的每行代码都要 review 吗?培养习惯真的不容易。

好了,管中窥豹而已,项目中的“非典型技术的技术问题”还有很多是不是?除了自动化我们就别想指望人来解决。

2

解决

类型1: 如果你根本没听说过 Lint,请赶紧 Google 一下 Android Lint。因为你是一个或者很可能会成为一个技术 Leader,如果到时候要靠人工审核这些,你的麻烦就大了去了;(初级-看一下整个第二大部分)

类型2: 如果你听说过Lint没有使用过,打开你的工程cd到主工程,再 ../gradlew Lint 一下。或者在 AndroidStudio Menu 中点击Analyse -> Inspect Code。因为知识不只停留在:“啊,我听说过”;(初级-看一下整个第二大部分)

类型3: 如果经常使用 Google 提供的 Lint,然而没有自定义过 Lint,这篇文章最适合你。因为关于自定义 Custom-Lint 资料不多,我也把好的资料地址都放进来了;

类型4: 如果你是自定义 Lint 高手,联系我 Email,交换一下学习心得,这个最难得。

二、

Lint检查基础

1

什么是 Lint 检查

版权说明:此部分概念性内容大部分摘抄自网络,并非原创,地址放在文章最后一部分参考资料中。

  • Android Lint 是一个静态代码分析工具,它能够对你的 Android 项目中潜在的 bug 、可优化的代码、安全性、性能、可用性、可访问性、国际化等进行检查。Android Lint 内置了很多 Lint 规则,到现在为止是230余项检查,总共可以分为以下几类:
    • Correctness 正确性
    • Security 安全性
    • Performance 性能
    • Usability 可用性
    • Accessibility 可访问性
    • Internationalization 国际化
  • 静态代码分析工具常被用来检测代码中的质量问题或者编码规范问题。Lint 作为最早的静态代码分析工具,已被用来作为静态代码分析工具的代名词。因此,Android SDK 也把其静态代码分析工具取名为 Android Lint。Android Lint 内置了很多 Lint 规则,用来检测一些常见的代码问题(例如,正确性问题、安全问题、性能问题,等等)。同时,Android Lint 也支持自定义 Lint 规则,以便开发者灵活应用,更好地提升项目代码质量。利用自定义 Lint 规则,既可以用来在项目中检测代码质量问题,也可以用来保证编码规范的执行。
  • 更直观的讲,我们平时代码写的疏漏,Java文件、xml 文件等等写的有问题时,第一时间报警给我们,编译时报错无法通过,这都是 Lint 在帮我们做检查。当然,这些检查都是 Google 默认帮我们写好的。 下面列举一些常见的lint会检测的代码问题:
    • 缺少翻译(和未使用的翻译);
    • 布局性能问题(老的 layout opt 工具会用于查找所有这样的问题,和除此之外更多的问题);
    • 未使用的资源;
    • 不一致的数组大小(当在多个配置中定义数组);
    • 可访问性和国际化问题(硬编码字符串,缺少 contentDescription 等);
    • 图标问题 (如丢失密度、 重复图标、 错误尺寸等);
    • 可用性问题 (如不在文本字段上指定输入的类型);
    • 清单错误。

2

为什么要使用自定义Lint检查

Google 提供的默认 Lint 检查很全面但是我们终归会有很多项目特性、自定义规则无法满足,如开头我提到的几个案例,这时候我们需要自定义 Lint。另外更深层的问题是要自动化检查取代人工检查的成本,提高生产效率,降低人工低级失误带来的负面影响,这个理论是自从工业革命开始就早已被确认的毋庸置疑了,没什么可争论的,不管自动化过程花费多长时间、多大精力,我们都要坚持。Google 在 Custom-Lint 上提供了强大的 API 支持我们,而且更新速度很快,只可惜相关文档还是比较少的。

3

自定义 Lint 入门 & Custom-Lint 核心API

说明:此部分可参见教程:自定义 Lint 规则简介,这里仅罗列大体思路和使用时的备注。

A. Gradle配置包

compile 'com.android.tools.lint:lint-api:25.2.0'
compile 'com.android.tools.lint:lint-checks:25.2.0'

至于使用的版本号,你可以查看一下最新的,请务必如此,我之前在写“FastJsonDetector”时,使用的是24.3.1版本,想查看某个类是否实现了某个接口,调查了很久而不得方法,结果发现新版本25.1.0里面新增了“getInterfaces”这个方法。所以希望大家尽量使用新版本API。

B. com.android.tools.lint.client.api.IssueRegistry

实现一个继承自此类的子类,他起到的作用是注册你有哪些检查要开放出去在 Lint 过程中被执行。

另外一定注意这个地方,要在 Gradle 配置上他才可以。

jar {
  manifest {
      attributes('Lint-Registry': 'com.qjoy.LFIssueRegistry')
  }
}

C. Detector 实例+ XXXScanner 接口

继承 Detector 并选择 Detector 中合适的 XXXScanner 接口来实现。在这里根据自身业务需求,实现各种自定义探测器(Detector ),并定义各种 issue,根据自身需求的不同这样的类可以有一个或多个。

其中,com.android.tools.lint.detector.api.Detector 提供了7种 XXXScanner 接口。

另外,利用 Context(此处的 Context 是 Lint 检查的类,不是 Android 的那个)的 report 方法报警,就会在错误日志中产生一条记录啦。

怎么样,是不是足够强大,检查所有你能想到的。

每种 XXXScanner 接口功能说明:

  • JavaScanner 功能:Specialized interface for detectors that scan Java source file parse trees
  • ClassScanner 功能:Specialized interface for detectors that scan Java class files
  • BinaryResourceScanner 功能:Specialized interface for detectors that scan binary resource files
  • ResourceFolderScanner 功能:Specialized interface for detectors that scan resource folders (the folder directory itself, not the individual files within it)
  • XmlScanner 功能:Specialized interface for detectors that scan XML files
  • GradleScanner 功能:Specialized interface for detectors that scan Gradle files
  • OtherFileScanner 功能:Specialized interface for detectors that scan other files

三、 AndroidLintWatchDog 中的自定义 Lint 检查

说明:最好看一下源码: GitHub follow&star。

我们选择两个我们实现的Detector简单分析一下,其余的请查看源代码吧:

1

BaseActivityDetector

目标:类是否继承自 LFBaseActivity 或者 LFBaseAppCompatActivity。

public class BaseActivityDetector extends Detector implements Detector.JavaScanner
{
...省略非核心代码...
public AstVisitor createJavaVisitor(JavaContext context) {
return new ForwardingAstVisitor() {
   @Override
   public boolean visitClassDeclaration(ClassDeclaration node) {
       ...核心代码...
       在这里分析node,检查通过或者报警
   }
}
}

由于是扫描 Java 代码内容,我们实现 JavaScanner,利用createJavaVisitor 接口的 visitClassDeclaration 扫描内容。

这里我们使用一个递归方法recursiveSupperClass查看父类,追溯直到checkActivityRules发现继承了 LFBaseActivity/LFBaseAppCompatActivity,或者直到发现直接继承了 Activity/AppCompatActivity,或者直接继承了 Object (说明他根本不是 Activity )。

2

ImageFileSizeDetector

目标:检查图片文件尺寸是否超过某个限定的大小。

public class ImageFileSizeDetector extends Detector implements Detector.BinaryResourceScanner {
...省略非核心代码...
@Override
public boolean appliesTo(ResourceFolderType var1) {
   return var1.getName().equalsIgnoreCase(String.valueOf(ResourceFolderType.MIPMAP)) ||        var1.getName().equalsIgnoreCase(String.valueOf(ResourceFolderType.DRAWABLE));
}
@Override
public void checkBinaryResource(ResourceContext context) {   String filename = context.file.getName();   ...核心代码...
   在这里分析node,检查通过或者报警
}
}

由于是扫描二进制资源,我们实现 BinaryResourceScanner,利用 BinaryResourceScanner 接口的 checkBinaryResource 扫描内容。 通过 ResourceContext 可以获取文件信息,用来做我们判断的条件。

最后,让我们看看执行效果吧:

参考资料与鸣谢

  1. 官方文档:lint-api 25.1.0版本 没有什么比读官方api文档更高效的方法了,这里看看官方最新的版本是什么,之后替换连接中的25.1.0查看最新的文档。
  2. GitHub-AndroidDevNotes 构建自定义lint检查整个工程什么样的结构;
  3. 教程:自定义 Lint 规则简介 内容基本同上第2条;
  4. 美团app-lint实现方案 一直很欣赏美团的开源精神和开源实力,大家不妨一起拜读一下吧,其实同2、3相似,但是插件话这个是上面没有做到的;
  5. google实现好的lint检查源代码 这个主要起到参考作用,官方的大牛200多个实例;
  6. a11n-android-lint 英文原文的;
  7. Github-yongce/AndroidArch 不错的实例程序;
  8. 教程-Android Lint 全面而系统讲了原理相关知识,应该是目前能搜索到的最高级的资料了。

五、

工程源码

工程源码托管在 GitHub follow&star。

小贴士

本文由原作者薛晴独家授权Open软件开发小组发布,著作权归原作者所有。如需转载请联系原作者申请授权。

本文分享自微信公众号 - Open软件开发小组(open_dev),作者:薛晴

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-06-22

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java设计模式精讲(一):责任链模式

    这篇分享作为一个系列分享的第一篇,主要和大家一起学习一下java设计模式方面的基础,我们现在的安卓开发主要还是基于java语言,所以掌握java...

    open
  • 重新认识AndroidStudio和Gradle,这些都是你应该知道的

    本文要从AndroidStudio的环境安装升级,Gradle,Eclipse转AS,多渠道配置,Maven私服,Action,Option,快...

    open
  • Android系统16ms之内能做些什么

    如果你觉得你的应用界面出现卡顿不流畅的情况,不用怀疑,这很大原因是你没有在16ms完成你的工作。没错,16ms要完成你的工作,再慢点,用户一定会吐槽,然后狠心...

    open
  • 《深入理解ES6》阅读笔记 --- babel

    在2017年相信ES6已经得到了很大的普及,如果你写过React或者Vue,相信在多年之前就已经体验过ES6的魅力了。言归正传,《深入理解ES6》阅读笔记并不会...

    icepy
  • PHP字符串格式化特点和漏洞利用点

    在PHP中存在多个字符串格式化函数,分别是 printf() 、 sprintf() 、 vsprintf() 。他们的功能都大同小异。

    猿哥
  • 第56节:ArrayList,LinkedList和String

    这样的话把 arrayList.remove( ) 中的数字改为 0, 1, 2, 的话,显示台会出现 1, 2, 3, 的.

    达达前端
  • ASP.NET Core中如影随形的”依赖注入”[下]: 历数依赖注入的N种玩法

    在对ASP.NET Core管道中关于依赖注入的两个核心对象(ServiceCollection和ServiceProvider)有了足够的认识之后,我们将关注...

    蒋金楠
  • Python数据分析---matplotlib可视化(堆叠柱状图-月份)

    偶然看到网上国家统计数据,利用Python数据分析自己做了几种图表练习。主要采用Pandas来做数据统计,matplotlib来做图表可视化。

    MiaoGIS
  • Python实力操作-网页正文转换语音文件

    天气真的是越来越冷啦,有时候我们想翻看网页新闻,但是又冷的不想把手拿出来,移动鼠标翻看。这时候,是不是特别想电脑像讲故事一样,给我们念出来呢?人生苦短,我有py...

    一墨编程学习
  • 怎么用 Python 来朗读网页 ?

    之所以用 Python,就是因为 Python 有着丰富的库,网页正文识别也不在话下。这里我尝试了 readability、goose3。

    小小詹同学

扫码关注云+社区

领取腾讯云代金券