Android项目优化检测工具Lint

前言

我们在实现Android界面功能之外还需要考虑结构对性能、维护成本的影响。例如,如果 XML 资源文件包含未使用的命名空间,则不仅占用空间,还会导致不必要的额外处理。其他结构问题,例如使用目标 API 版本不支持的已弃用的元素或 API 调用等,可能导致代码无法正常运行。Android Studio 提供一个名为 Lint 的代码扫描工具,可帮助您发现并纠正代码结构质量的问题,而无需实际执行该应用,也不必编写测试用例。

Lint工作流程

该工具会报告其检测到的每个问题并提供该问题的描述消息和严重级别,以便您可以快速确定需要优先进行哪些关键改进。此外,您可以调低问题的严重级别,忽略与项目无关的问题,也可以调高严重级别,以突出特定问题。

Lint 工具可检查您的 Android 项目源文件是否包含潜在错误,以及在正确性、安全性、性能、易用性、便利性和国际化方面是否需要优化改进。

Lint 工具的代码扫描工作流

可以看到源文件(包括Java和XML)、图标、配置文件等,通过 lint.xml 中的问题严重级别配置,然后执行 Lint 命令工具对代码静态扫码。

配置Lint文件

lint.xml 文件由封闭的 <lint> 父标记组成,此标记包含一个或多个 <issue> 子元素。Lint 为每个 <issue> 定义唯一的 id 属性值。

在Android项目的根目录新建 lint.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
    <lint>
        <!-- list of issues to configure -->
</lint>

但是我们通常不这么配置,可以在 Android Studio 中进行设置,选择 File > Other Settings > Default Settings,然后在 Default Preferences 对话框的左侧窗格中选择 Editor > Inspections。

Inspections设置界面

Inspections 翻译过来是检查的意思,所以这个设置里面是 Android Studio 的一些检查包括编译的检查和等级设置,当然也包括了对Lint的检查等级设置。用鼠标点击每一项右侧都会有对应的说明,设置分为10类,分别是 Accessibility(无障碍), Compliance(遵从), Correctness(正确性), Internationalization(国际化), Interoperability(互操作性), Lint(基本设置), Performance(性能), Security(安全), Testing(测试), Usability(可用性), 常用的几项的Lint的设置归纳如下:

  • Accessibility in Custom Views 自定义视图中的辅助功能

如果覆盖onTouchEvent或使用OnTouchListener的视图在检测到单击时也没有实现performClick并调用它,那么视图可能不能正确地处理可访问性操作。处理单击操作的逻辑应该放在view# performClick中,因为当发生单击操作时,一些可访问性服务调用performClick。

  • Image without contentDescription 没有内容描述的图像

像ImageViews和ImageButtons这样的非文本小部件应该使用contentDescription属性来指定小部件的文本描述,以便屏幕阅读器和其他可访问性工具能够充分描述用户界面。注意,应用程序屏幕中的元素是纯装饰的,不提供任何内容或使用户操作不应该具有可访问性内容描述。在这种情况下,只需使用工具来抑制lint警告:忽略=“ContentDescription”属性。注意,对于文本字段,不应该同时设置提示和contentDescription属性,因为提示永远不会显示。只是设置提示。

  • Keyboard inaccessible widget 键盘访问小部件

被声明为可点击但未声明为可调焦的小部件不能通过键盘访问。请添加可调焦属性。

  • Missing accessibility label 丢失的可访问性标签

可编辑的文本字段应该提供一个android:提示或者,如果您的minSdkVersion至少是17,那么可以使用android:labelFor属性来引用它们。

  • Overriding getContentDescription() on a View 在View中覆盖getContentDescription方法

  • Extraneous text in resource files 资源文件中的无关文本

布局资源文件应该只包含元素和属性。文件中发现的任何XML文本内容都可能是偶然的(如果文本类似于XML,并且开发人员认为文本是功能性的,则可能是危险的)

  • Hardcoded reference to /sdcard 硬编码/ sdcard引用

您的代码不应直接引用/sdcard路径;而不是使用Environment.getExternalStorageDirectory().getPath()。同样,不要直接引用/data/data/ path;它可以在多用户场景中变化。相反,使用Context.getFilesDir().getPath()。

  • Layout Inflation without a Parent 没有Parent的Layout Inflation

在膨胀布局时,避免将null作为父视图传递,否则膨胀布局的根上的任何布局参数都将被忽略。

  • Mismatched Styleable/Custom View Name 不匹配Styleable /自定义视图的名称

自定义视图的约定是使用一个声明式样式表,其名称与自定义视图类名匹配。IDE依赖于这种约定,例如,可以为布局XML资源文件中的自定义视图中的属性提供代码补全。(类似地,布局参数类应该使用后缀_Layout。)

  • Missing commit() calls 失踪的commit()调用

创建碎片事务之后,通常还需要提交它

  • Nested scrolling widgets 嵌套的滚动窗口小部件

滚动小部件(比如ScrollView)不应该包含任何嵌套滚动小部件,因为这有各种可用性问题

  • Obsolete Gradle Dependency 过时Gradle依赖

这个检测器查找您正在使用的版本不是当前稳定版本的库的用法。使用旧版本是可以的,而且在某些情况下,您故意希望坚持使用旧版本。但是,您可能根本没有意识到有更新的版本可用,这就是lint check帮助查找的内容。

如果细心的朋友会发现,上面每一个Lint的设置的解释下面都有一个 Issue id, 如果我们要在 lint.xml 文件中配置就需要指定对应的id来表明设置的项目,例如:

<?xml version="1.0" encoding="UTF-8"?>
<lint>
    <!-- Disable the given check in this project -->
    <issue id="IconMissingDensityFolder" severity="ignore" />

    <!-- Ignore the ObsoleteLayoutParam issue in the specified files -->
    <issue id="ObsoleteLayoutParam">
        <ignore path="res/layout/activation.xml" />
        <ignore path="res/layout-xlarge/activation.xml" />
    </issue>

    <!-- Ignore the UselessLeaf issue in the specified file -->
    <issue id="UselessLeaf">
        <ignore path="res/layout/main.xml" />
    </issue>

    <!-- Change the severity of hardcoded strings to "error" -->
    <issue id="HardcodedText" severity="error" />
</lint>

除了上面的配置和 Android Studio的设置外,我们还可以通过 gradle 的 lintOptions {} 配置 Lint 项。

android {
  ...
  lintOptions {
    // Turns off checks for the issue IDs you specify.
    disable 'TypographyFractions','TypographyQuotes'
    // Turns on checks for the issue IDs you specify. These checks are in
    // addition to the default lint checks.
    enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
    // To enable checks for only a subset of issue IDs and ignore all others,
    // list the issue IDs with the 'check' property instead. This property overrides
    // any issue IDs you enable or disable using the properties above.
    check 'NewApi', 'InlinedApi'
    // If set to true, turns off analysis progress reporting by lint.
    quiet true
    // if set to true (default), stops the build if errors are found.
    abortOnError false
    // if true, only report errors.
    ignoreWarnings true
  }
}

运行Lint检测

命令方式

命令格式:

lint [flags] <project directory>

例如我们扫描 myproject 目录及其子目录的文件,查看是否具有Android命名空间前缀的xml属性。

lint --check MissingPrefix myproject 

其中 MissingPrefix 是 Issue id.

通过gradle方式

在windows上使用 gradlew lint 在 Linux 或 Mac 上使用 ./gradlew lint

如果您只想为特定构建变体运行 lint 任务,您必须大写变体名称并在其前面加上 lint 前缀, 例如: gradlew lintDebug

手动方式

可以选择 Inspect Code > Analyze,手动运行已配置的 Lint 和其他 IDE 检查。检查结果显示在 Inspection Results 窗口中。

操作步骤如下:

  1. 在 Android 视图中,打开项目并选择此项目以及您要分析的文件夹或文件。
  2. 从菜单栏选择 Analyze > Inspect Code。
  3. 在 Specify Inspection Scope 对话框中查看设置。

查看检查范围设置

  1. 在 Inspection Profile 中,保留默认的配置文件 (Project Default)。
  2. 点击 OK 以运行检查。

检测结果示例

可以直接在这里处理错误和警告,当然还有更加方便的是从这里点设置图标可以去设置 Edit Settings,然后在执行 Rerun 重新检测。

配置忽略

对Java文件

要在 Android 项目中特别禁止 Lint 检查某个 Java 类或方法,请向此 Java 代码添加 @SuppressLint 注解。

下例说明了可以如何对 onCreate 方法中的 NewApi 问题关闭 Lint 检查。Lint 工具会继续检查该类的其他方法中的 NewApi 问题。

@SuppressLint("NewApi")
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
}

下例说明了如何对 FeedProvider 类中的 ParserError 问题关闭 Lint 检查:

@SuppressLint("ParserError")
public class FeedProvider extends ContentProvider {

}

要禁止检查 Java 文件中的所有 Lint 问题,请使用如下 all 关键字:

@SuppressLint("all")

对XML文件

您可以使用 tools:ignore 属性禁止 Lint 检查 XML 文件的特定部分。在 lint.xml 文件中添加以下命名空间值,以便 Lint 工具能识别此属性:

namespace xmlns:tools="http://schemas.android.com/tools"

下例说明了可以如何禁止 Lint 检查 XML 布局文件的 <LinearLayout> 元素中的 UnusedResources 问题。如果某个父元素声明了 ignore 属性,则该元素的子元素会继承此属性。在本例中,也会禁止 Lint 检查 <TextView> 子元素。

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:ignore="UnusedResources" >

    <TextView
        android:text="@string/auto_update_prompt" />
</LinearLayout>

要禁止检查多个问题,请使用以逗号分隔的字符串列出要禁止检查的问题。例如:

tools:ignore="NewApi,StringFormatInvalid"

要禁止 Lint 检查 XML 元素中的所有问题,请使用如下 all 关键字:

tools:ignore="all"