1 Android Lint介绍
1.1 Google开发者文档
先来段developer的官方引用:
Android Studio provides a code scanning tool called Lint that can help you to easily identify and correct problems with the structural quality of your code, without having to execute the app or write any test cases.
该图诠释了Lint工具是如何检测应用源代码的:
1.2 各类Android博客
1.2.1 CSDN博客专家-张兴业
作为移动应用开发者,我们总希望发布的apk文件越小越好,不希望资源文件没有用到的图片资源也被打包进apk,不希望应用中使用了高于minSdk的api,也不希望AndroidManifest文件存在异常,lint就能解决我们的这些问题。
1.2.2 美团点评技术团队
Android Lint是Google提供给Android开发者的静态代码检查工具。使用Lint对Android工程代码进行扫描和检查,可以发现代码潜在的问题,提醒程序员及早修正。
为保证代码质量,美团在开发流程中加入了代码检查,如果代码检测到问题,则无法合并到正式分支中,这些检查中就包括Lint。
其他博客就不一一列举了。
2 Android Lint使用
2.1 使用前的相关术语
再来段官方文档:
The Lint tool checks your Android project source files for potential bugs and optimization improvements for correctness, security, performance, usability, accessibility, and internationalization. You can run Lint from the command-line or from Android Studio.
简要来说:
布局性能(以前是 layoutopt工具,可以解决无用布局、嵌套太多、布局太多)
未使用到资源
不一致的数组大小
国际化问题(硬编码)
图标的问题(重复的图标,错误的大小)
可用性问题(如不指定的文本字段的输入型)
manifest文件的错误
内存泄露 --- 如:handle的不当使用 。
占内存的资源及时回收 --- 如:cursor未关闭等
2.1.1 lint.xml
Lint检查的配置文件,该文件即为你修改的某些Lint配置项,其需要在对应模块的根目录,Lint Tool会自动扫描模块目录下是否有对应的lint配置文件。
该lint文件如下所示:
其中severity是严重级别:error,ignore,warning。注意其也可以只指定部分文件
2.2 Java和xml间的配置
2.2.1 java层
@SuppressLint
注解,其允许在任何地方定义,如在方法体上定义,则忽略该方法体的Lint检测。
@SuppressLint("NewApi")
最为常见,@SuppressLint("all")
意为忽略所有规则。 2.2.2 xml层
在xml文件,首先引入:
namespace xmlns:tools="http://schemas.android.com/tools"
简单介绍下tool,用于告知as,哪些属性可被忽略,只在设计布局时有效:
Lint提示。
tools:ignore = "contentDescription"
意为忽略lint的imageview的contentDescription属性,该属性用于视力障碍者。-
预览布局。
tools:text
如某控件在Java代码层动态设置text属性,而你需要看预览效果,通常会设置android:text="I am a title"
,等预览完毕再进行删除,使用tools,则完全达到预览效果。tools:context
预览布局时,ide可知采用哪种主题,还可Go to Related files。
具体tools分析:
2.3 AS使用指南
分析了那么多,意在说明Lint工具的重要性,那么下一阶段则是如何在as中使用,以及配置,以及结合公司代码进行项目检查。
2.3.1 使用GUI
Analyze”菜单中选择“Inspect Code”,其中可以选择scope,即检测范围,也可以选择不同的检测配置,我们先进行默认的配置检测吧。检测需要一定的时间,结果会在EventLog显示:
可以看到,我们的项目有很多问题,比如我选择了Correctness中的Using dp instead of sp for text sizes属性,发现应用中有2处在textSize中误用了dp,其给出了类的具体位置和解决方案。可能你觉得这些无关大雅,那么可以查看Probable bugs项,在其中找到一项 String comparison using '==',instead of 'equals()',可以看到SecurityBankCardListActivity类中的有一行代码:
this.mBankCard.getCardId() == mBankCard.getCardId()//cardId为String类型
在此就不一一列举。
可能你会觉得Lint分析的太过详细,我无法迅速找到问题,那么你可以点击,其分为四类,我们应只关注前2类。
2.3.2 使用命令行
运行gradle lint
,然后在app/build/outputs打开lint-results.xml
2.3.3 AS的Lint配置
打开设置对话框,找到Editor,然后是Inspections,选择某一个Lint选项,修改严重等级,如图:
gradle中的Android插件的lintOptions,具体lintOptions属性,可参照
3 为什么需要自定义
我们在实际使用Lint中遇到了以下问题:
原生Lint无法满足我们团队特有的需求,例如:编码规范。
原生Lint存在一些检测缺陷或者缺少一些我们认为有必要的检测。
基于上面的考虑,我们开始调研并开发自定义Lint。
那我们的项目需要自定义Lint吗,我觉得很有必要,举几个例子:
我们有MuLog模块,但是仍然有开发同事会使用android自带的Log工具类或java的System.out.print,显然我们应该可以选择强制开发同事调用MuLog,如果调用Log等,编译器会报错等提示。
-
增强HashMap检测,Lint检测中有一项是Java性能检测,常见的就是:HashMap can be replaced with SparseArray。例如下面代码,原生Lint只能检测第一种情况。
public static void testHashMap() { HashMap
map1 = new HashMap (); map1.put(1, "name"); HashMap map2 = new HashMap<>(); map2.put(1, "name"); Map map3 = new HashMap<>(); map3.put(1, "name");} Intent携参,activity页面间的跳转需要intent,而需要传输数据时,需要extra,对应的该extra都需要一个key,该key应该是跳转类中的命名常量,而不是写死在
putExtra("key","value")
里,我们可以强制开发同事Intent携参的key必须为按照EXTRA_<name>格式命名的常量。比如Android由于性能的原因应该尽量减少或避免使用枚举。
比如线程的创建不应该是简单的new Thread,而是应该使用同一的全局的线程池。
3.1 自定义教程
参看美团自定义Lint实践文章,具体流程如下所示:
3.1.1 google的自定义方案
根据该文章,将写好的jar拷贝到android/lint文件夹下,再下一次运行IDE时,IDE会检测其内的jar文件。
缺点:开发人员的机器上所有的Android工程都会受到影响,而且每个开发人员间的拷贝成本大。
3.1.2 LinkedIn方案
LinkedIn提供了另一种思路 : 将jar放到一个aar中。这样我们就可以针对工程进行自定义Lint,lint.jar只对当前工程有效。
aar文件中放入lint.jar是可选的。该lint.jar将会通过app的lint task被执行。
所以我们选择该方案。
3.1.3 自定义gradle plugin
aar虽然很方便,但是在团队内部推广起来,依然很困难。
配置繁琐,不易推广。每个库都需要自行配置lint.xml、lintOptions,并且compile aar。
不易统一。各库之间需要使用相同的配置,保证代码质量。但现在手动来回拷贝规则,且配置文件可以自己修改。
于是我们想到开发一个plugin,统一管理lint.xml和lintOptions,自动添加aar。
3.1.4 自定义plugin后
集成了原生lint和自定义lint的所有检查规则
内置lintOptions(所有warning视为error,只输出htmlReport到${project.projectDir}/lint-report/lint-report.html)和 lint.xml。集成此插件后,原有配置会被覆盖,没有配置的也会执行插件中的配置
此处错误,仅仅是IDE提示开发人员需要进行修改,其仍然可以提交该代码并运行该代码。
3.1.5 新建项目
创建lint的java项目,包含的是自定义的lint规则
创建lint的library项目,仅用来输出包含lint.jar的aar
创建Android项目,用来测试自定义lint规则
创建groovy项目,用于生产plugin
3.1.5 svn的pre-commit
svn的pre-commit如何进行触发lint检查,以便开发人员每次提交代码时,都会启动lint检查提交的代码是否包含自定义Error,并拒绝其提交该代码。
项目融合
-
project中的build.gradle文件的buildscript的dependencies中添加:
classpath 'com.mucfc.muna.lint:plugin:latest.integration'
-
在module中,如muapp中添加:
apply plugin: 'MTLintPlugin'
上述代码完成后,所有的开发人员当编写相关的代码时,如Log.d()都会出现错误提示,而且不会影响项目。
自定义lint规则
现阶段,我已经上传到公司maven库的有:plugin和aar。所以如果项目引用该plugin,则针对业务需要可以逐渐扩展该lint规则。
svn的提交前代码检查脚本。
参考文献
美团技术:
LinkedIn Engineering:
Google关于lint.jar:
Google blog:
yongce-AndroidDevNotes: