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

by QJoy (email alexq_andr@163.com

工程源码托管在 GitHub follow&star

watchdog-logo

一、解决痛点-coding时遇到的问题

案例

  • 案例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吗?培养习惯真的不容易。

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

SO

  1. 类型1: 如果你根本没听说过lint,请赶紧google一下android lint。因为你是一个或者很可能会成为一个技术leader,如果到时候要靠人工审核这些,你的麻烦就大了去了;(初级-看一下整个第二大部分)
  2. 类型2: 如果你听说过lint没有使用过,打开你的工程cd到主工程,再../gradlew lint一下。或者在AndroidStudio Menu中点击Analyse -> Inspect Code。因为知识不只停留在:“啊,我听说过”;(初级-看一下整个第二大部分)
  3. 类型3: 如果经常使用google提供的lint,然而没有自定义过lint,这篇文章最适合你。因为关于自定义custom-lint资料不多,我也把好的资料地址都放进来了;
  4. 类型4: 如果你是自定义lint高手,联系我email,交换一下学习心得,这个最难得;

二、Lint检查基础(3/4类型的可跳过此部分)

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会检测的代码问题:

    • 缺少翻译(和未使用的翻译)
    • 布局性能问题(老的layoutopt工具会用于查找所有这样的问题,和除此之外更多的问题)
    • 未使用的资源
    • 不一致的数组大小(当在多个配置中定义数组)
    • 可访问性和国际化问题(硬编码字符串,缺少contentDescription等)
    • 图标问题 (如丢失密度、 重复图标、 错误尺寸等)
    • 可用性问题 (如不在文本字段上指定输入的类型)
    • 清单错误

2. 为何要使用自定义Lint检查

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

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

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

  1. 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。

  2. com.android.tools.lint.client.api.IssueRegistry
    实现一个继承自此类的子类,他起到的作用是注册你有哪些检查要开放出去在lint过程中被执行。
    另外一定注意这个地方,要在gradle配置上他才可以。

    jar {
        manifest {
            attributes('Lint-Registry': 'com.qjoy.LFIssueRegistry')
        }
    }
    
  3. Detector实例+XXXScanner接口 (可参考:浅谈Android自定义Lint规则的实现
    主人公来了:继承Detector并选择Detector中合适的XXXScanner接口来实现:在这里根据自身业务需求,实现各种自定义探测器(Detector),并定义各种issue,根据自身需求的不同这样的类可以有一个或多个。

    com.android.tools.lint.detector.api.Detector提供了7种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

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

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

三、AndrLintWatchDog中的自定义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可以获取文件信息,用来做我们判断的条件。

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

    example-cmd

    example-htmlreport

四、参考资料与鸣谢

  1. 官方文档:lint-api 25.1.0版本没有什么比读官方api文档更高效的方法了;(https://bintray.com/android/android-tools/com.android.tools.lint.lint)这里看看官方最新的版本是什么,之后替换连接中的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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,563评论 25 707
  • afinalAfinal是一个android的ioc,orm框架 https://github.com/yangf...
    passiontim阅读 15,078评论 2 44
  • Android Lint 是有 Android SDK 提供的一种静态代码检测工具,用于检测 Android 的代...
    灰灰鸽阅读 10,099评论 1 15
  • 前言: 本篇文章是iOS Animation 的阅读总结,其中去除了比较多的琐碎的解释,更多的是在大概知道Core...
    BBH_Life阅读 430评论 0 0
  • 好几年前,在听某个国外的大拿讲专业知识时,就见识了在屏幕上边画边讲,讲完也画完,最后留下一张纸囊括所有要点。奉为神...
    在路上vicky阅读 451评论 1 5