Writing Custom Lint Rules(半读半就版)

官网原址

本文只是个人为了整理Lint相关知识,依据谷歌翻译整理而成,水平确实不堪,很多地方自己都读不太懂,见谅...

新的2015年8月27日:现在有一个最新的示例项目,显示了如何编写自定义lint检查-包括单元测试
https://github.com/googlesamples/android-custom-lint-rules
该代码库编写一个lint规则比下面写上去包含的代码有更好的基础。


Lint 在ADT20上预先配置了大概100中检查。但是,也可以使用附加规则进行扩展。例如,如果你是库项目的作者,并且你的库项目具有一定的使用要求,则可以编写额外的lint规则去检查你的库是否正确使用,然后你可以向这个库项目的用户分发这些额外的lint规则。同样,您可能需要执行公司本地的规则。

本文介绍如何编写自定义lint规则。首先,请参阅Google I/O 2012“开发工具的新功能”中的以下演讲,用一个简短的demo了解如何编写和使用lint规则。(跳转至视频55分钟18秒,如果嵌入式播放器未自动跳转。)
(...................................内嵌视频好高端啊,诸位请自行官网............................................)

以下是该视频的详细信息。

为Android Studio创建自定义规则项目


首先,你需要创建一个单独的java项目去编译这个lint 规则。请注意,这是一个java项目,而不是Android项目,因为此代码将被运行在Android studio,IntelliJ,Eclipse,或Gradle,或命令行lint工具中运行--而不是在一台设备上运行。

最简单的方法是从示例项目开始,解压它,然后根据需要更改源代码。你可以在Android Studio或者IntelliJ通过指向要导入的build.gradle项目.

这是一个Gradle项目,它具有使用Lint APIs的所有依赖项,并使用正确的清单条目构建.jar文件。

为Eclipse创建自定义规则项目

如果你使用Eclipse,使用一个简单的库创建一个简单的java项目。

接下来,添加 lint_api .jar文件到项目的classpath。这个jar文件包含规则实现的Lint APIS,你可以在sdk的安装目录 tools/lib/lint_api.jar中找到lint_api jar包

创建检测器

当你实施“lint规则”时,你将准备实现一个“检测器”,它可以识别一个或多个不同类型的问题。这种分离使得单个检查器识别与逻辑相关的不同类型的问题,但是可能具有不同的严重性,描述,并且用户可能想要独立的抑制。例如,清单检测器查找单独的问题,如不正确的注册顺序,缺失minSdkVersion声明等。有关如何编写lint检查的更多详细信息,请参阅编写Lint Check文档。

在我们的示例中,我们假设我们有一个自定义View,并且我们想要制定一个lint 规则,确保这个自定义View的所有使用处都定义了这个特定的View属性。

这里是完整的检测器的源代码:


首先你可以看到这个检测器继承ResourceXmlDetector。这是一种用于资源xml文件如布局和字符串资源声明的检测器。还有其它类型的检测器,例如处理java 源代码或者字节码的检测器。

getApplicableElements()方法返回一组当前检测器关心的xml标签。这里我们只返回了我们自定义view的标签。lint基础设施将为每次出现的标签调用visitElement()方法--并且仅针对该标记的出现。在visitElement()方法中,我们简单的看当前的元素是否定义了我们自定义的属性(“exampleString”),如果没有,我们就报告一份错误。

visitElement方法传递一个context对象。这个context提供很多相关的context;例如,你可以查找相应的项目(从这儿请求minSdkVersion或者targetSdkVersion),或你可以查找正在被分析的xml文件的路径,或你可以创建一个错误的位置(如此处所示)。在这里,我们传递这个元素,这将使错误指向元素的开头,但是你可以例如传递一个xml属性对象,这将指向一个特定的属性。

再说一次,查看Writing a Lint Check文档以获取更多的细节。

创建问题

report()方法第一个参数是ISSUE。这是该检测器报告问题的引用。这是我们在上面类中顶部定义的静态字段。

一个问题有几个不同的属性,按以上顺序定义:

      ◆id,这是一个与这个问题有关的常量,应该是简短和描述性的;这个id例如用于java抑制注释和
        xml抑制属性来标识这个问题。
      ◆摘要。这应该是问题的简要(单行)摘要,例如在Eclipse中的Lint Options UI和其它简要描述
        这个问题的地方。
      ◆说明。这是对问题的更长的说明,这应该向lint的用户解释这是什么问题。通常一次 lint 的错误
        信息是简短的(单行),并且有时候很难在单行错误信息中完全解释一个细的问 题,所以该说
        明用于提供更多的上下文。该说明在lint命令行工具创建的完整HTML报告中显示,在Eclipse
        Lint窗口中显示当前选定的问题等。

      ◆类别。这里有许多预定义的类别,类别可以嵌套(例如可用性>图标),并且这有助于
        用户过滤和排序问题,或者通过命令行仅运行某些类型的问题。最常见的类别   
        是“correctness”和"performance",但是也包括国际化,可访问性,可用性等。
      ◆优先权。优先级是1-10之间的整数,其中10是最重要的,并且这被用于排序相应每一个其
        它优先级的问题。
      ◆严重性。一个问题可以有严重的,错误,警告,或忽略默认的严重性。请注意我所说的默
        认:用户可以通过一份lint.xml 文件(更多信息)覆盖问题所使用的严重性。严重和错误在
        eclipse中作为“错误”展示,但是严重的问题在用户想要在Eclipse当中打包一份APK的任何
        时候是自动运行的(没有用户的介入),并且如果它们中的任何一个发现错误,然后这个
        打包过程将被停止。“忽略”严重性的规则不运行。
      ◆检测器类。这只是指向负责识别问题的检测器,这应该指向我们自己的类。请注意,我们
        通过Lint自动实例化检测器,这是为每次运行完成的。因此,你不需要担心在运行之后清除
        检测器中的实例状态。在Eclipse中(其中Lint可以运行多次),将为每次运行创建一个新
        的检测器。因此,你的lint规则必须有一个公有的默认构造器。(如果没有指定,这个编译
        器将自动为你创建一个,就是上面的例子
      ◆检测范围。这决定了这个问题适用于哪些类型的文件。这里我们只是声明我们应用于xml文
        件,但未使用的资源问题的范围包含java和xml文件。

你还可以在问题上调用其它方法,例如,将问题定义为默认禁用,或设置“更多信息”URL。

我们已经创建了一个问题的实例。内置的lint问题都在BuiltinIssueRegistry类中注册。然而,对于自定义规则,我们需要提供我们自己的注册表。每一个自定义jar文件提供它们自己问题的注册类,其中每个问题注册表可以包含由给定jar文件标识的一个或多个问题。

这是我们问题的自定义注册表:

getIssues()方法返回的是由此注册表提供的问题列表。我们在这种特殊的情况下明显可以使用Collections.singletonList()来代替Arrays.asList(),但是这里通常会有多个问题。请注意,注册表必须具有公共默认构造参数,以便它能够被lint实例化。

注册注册表

最后一件事情我们需要去做的是注册这个问题注册表,以便可以由lint找到。我们通过编辑jar文件的清单文件来做到这一点。

如果你是在Gradle/Android studio项目中使用它,则由Gradle处理;只要打开build.gradle文件并根据需要更新jar条目中注册表的路径。

在Eclipse,创建一个清单文件,如下所示:

然后从你使用了上面清单文件的项目中导出一个jar文件。(我在Eclispe当中遇到了一些困难;导出jar对话框中有明确的选项可以做到这一点,但是当我查看导出的.jar文件并打开其中的manifest文件,它不包括我上面的两行,所以我手动创建jar文件:jar cvfm customrule.jar META-INF/MANIFEST.MF googleio)

注意:Eclipse中的导出程序期望在第二行之后添加换行符。所以,确保最后有一个空行,然后直接从Eclipse中导出应该正常工作。

注册自定义jar文件

现在我们有自己自定义规则.jar文件。当lint运行时,它将在~/.android/lint/目录查找自定义规则jar文件,因此我们需要将其放在那里:

(有人问过这个;Lint基本上调用了一般的Android工具方法来找到“工具设置目录”,用于模拟器快照,ddms配置等。你可以在这里找到相关的代码:https://android.googlesource.com/platform/tools/base/+/master/common/src/main/java/com/android/prefs/AndroidLocation.java)

搜索位置

android目录的位置通常是主目录;lint将在$ANDROID_SDK_HOME,${user.home}(java属性)和在$HOME中搜索,只是为了澄清。Lint按顺序搜索第一个存在的位置:
◆ANDROID_SDK_HOME(系统环境或环境变量)
◆user.home(系统环境)
◆HOME(环境变量)

所以,如果ANDROID_SDK_HOME存在的话,然后user.home和HOME将被忽略!这可能看起来是直观的,但是原因是ANDROID_SDK_HOME并不指向你的SDK安装路径;环境变量为ANDROID_SDK。最近的代码也在寻找ANDROID_HOME。所以,ANDROID_SDK_HOME真的是一个替代的主目录位置,当你指向特定的模拟器AVDs等进行单元测试时,在构建服务器上使用。这个命名是非常不幸的,并且导致了各种错误,但是很难改变,而不会破坏用户过去已经依赖的记录行为。现在,确保你不会使用环境变量ANDROID_SDK_HOME来指向你的sdk安装目录。

之后,它查找子路径/.android/lint/,即其中一个对应于刚找到的位置。
◆ANDROID_SDK_HOME/.android/lint/
◆user.home/.android/lint/
◆HOME/.android/lint/
and loads any JAR files it finds there.

并加载在那里找到的任何jar文件。

因此,如果你在〜/ .android / lint /和ANDROID_SDK_HOME存在时有 Lint jar文件,你的JAR 文件将不会被读取。 

如果你在~/.android/lint/和ANDROID_SDK_HOME不存在时有Lint JAR文件,你的JAR文件将被读取。

如果ANDROID_SDK_HOME存在,将你的Lint JAR 文件放到ANDROID_SDK_HOME/.android/lint/

运行自定义检查

最后,让我们运行lint去确保它知道我们的规则:

如果这不起作用,通过确保你的jar文件在正确的位置来解决这个问题,它包含在清单配置文件中正确注册的行数,完整的包名称和类名称是正确的,它是可实例化的(公有的默认构造器),并注册您的问题。

最后,让我们来测试这条规则,这是一个示例布局文件:

现在让我们在包含上面布局的项目中运行lint(并使用 --check MyId参数,我们仅检查我们自己的问题):

使用Jenkins /其他构建服务器

如果你作为正在连续构建的一部分运行lint,而不是将自定义lint规则放在构建服务器使用的账户主目录当中,你还可以使用环境变量$ANDROID_LINT_JARS来指向包含要是用lint规则的jar文件的路径(使用File.pathSeparator来分隔,例如在Windows和其它地方)。更多关于Jenkins+Lint的使用信息,请查看https://wiki.jenkins-ci.org/display/JENKINS/Android+Lint+Plugin

更复杂的规则

上面的检查非常简单。还有更多的可能。一个真正有用的资源指出如何更多的查看存在的100左右的问题。查看SDK git信息库,在sdk/lint/,特别是sdk/lint/dl/lint/libs/lint_checks/src/ com / android / tools / lint / checking。

贡献规则

自定义lint规则支持在lint当中主要用于编写真正的本地规则。如果你想到一般感兴趣的Lint检查,你可以使用上述方法来开发和检查规则。然而,请考虑将规则归因于AOSP,以便所有的Android开发者可以从检查中受益!请参阅合作页

源代码

在Gradle/Android Studio/IntelliJ中使用

this sample project

和在 Eclipse中使用

this .zip

在Eclipse当中打开项目之前,上述自定义lint规则和 lint_api.jar包复制到项目的根目录。

更多信息

如果你有Google+账号,你可以在此发表评论或提问。https://plus.google.com/u/0/116539451797396019960/posts/iyH3ER3LJF7

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,825评论 25 707
  • 立冬立冬,艳阳高空, 徒有夏气,不见夏风。 日渐西西,水泛东东, 片帆掠影,清波卧弓, 地冒排齿,岸衔潜龙, 镜涌...
    疯界阅读 238评论 0 0
  • 1. Qt串口通信类QSerialPort 在Qt5的的更新中,新增了串口通信的相关接口类QSerialPort,...
    Jiatian阅读 4,378评论 0 5
  • 读小说的乐趣,就是在想象中经历一段不可能;翻译小说的乐趣,就是在想象中把这段不可能当中的每个细节都经历一遍,并且在...
    joyli阅读 2,187评论 2 1
  • 一个男人的——怨念。 他对他心里的姑娘,百般示好而不得芳心。 某一天他邀请一个姑娘去看演唱会,没想到姑娘答应了。 ...
    逝者君阅读 212评论 0 0