An unofficial guide to Android Lint

Martin Bonnin
ProAndroidDev
Published in
3 min readOct 20, 2019

--

Android Lint is a great tool to catch structural errors and improve your Android codebase. Unfortunately, the documentation can be a bit sparse and does not go in the details of precedence and XML options. We’ll try to do that here.

Library by Kotomi_

The different flavors of lint

Android lint (source code) is the engine that performs the check. Its name come from the original 1978 C lint used to analyze C source files. Today lint is the umbrella term for tools analyzing source files and you’ll find ESlint, JSlint, pylint and other for other languages. Ktlint is a thing too, which you can read about there.

For Android, the lint engine can be invoked in different ways:

  1. From the command line (cli): you will find the command under $ANDROID_HOME/tools/bin/lint
  2. From gradle: ./gradlew :app:lint.
  3. From intelliJ/Android Studio: the suggestions and helpful yellow underlines.

This guide will focus on invoking lint from gradle. IDE/cli are slightly different and will most likely require more configuration and/or not report the same results and I haven’t looked into this yet.

Invoking lint from gradle

You can invoke lint in multiple ways:

  1. ./gradlew lint:will run lint on all modules for all variants.
  2. ./gradlew :app:lint:will run lint on the app module for all variants.
  3. ./gradlew :library:lint:will run lint on the library module for all variants.
  4. ./gradlew :app:lintFlavorDebug:will run lint on the app module the flavorDebugvariant.

Unless you have a lot of different code in your different flavors, solution 4 will save you some time. You still want lint to check the library module too. Too do that, make sure to set checkDependencies = true or you will miss some warnings:

Lint Checks

Lint contains a number of checks for issues that are each identified by an “id”. You can get the list by typing $ANDROID_HOME/tools/bin/lint --show. (Note how this runs the lint bundled with your Android SDK and not the gradle one, which might be a different version. We will assume they’re are not too different for now. Else you can always check the source code in the google repo).

All issues have:

  • An id (“MissingTranslation” here)
  • A Severity (“fatal”, “error”, “warning”, “informational” or “ignore”)
  • A priority between 1 and 10
  • A category
  • A default state: whether this issue is checked by default or not. Disabled issues will be marked with NOTE: This issue is disabled by default!

Configuration

Lint is highly configurable. The number of options and different combinations is huge. I think one of the keys to understand how configuration happens is to realize that most of parameters are actually stateful and some override each other. The order in which you declare them is important.

Determining exactly what is going to happen with precision will certainly require digging into the source code. But the below rules should give a good enough approximation for most use cases.

Enabling/Disabling checks

Controlling severity

Controlling severity is more complex. The different options are evaluated in this order:

  1. Default severity from lint itself.
  2. XML value
  3. Individual override from gradle ( fatal(), error(), …)
  4. Global override from gradle ( ignoreWarnings, warningsAsErros, …)

Other options you should certainly enable

XML configuration syntax

The source code gives us some hints of how to write the lint.xml file. The order of the nodes is important!

Wrapup

Lint is a powerful tool that allows to increase the code quality. The configuration took some time but this is definitely worth it and it has already helped us fix actual bug! I really hope the discrepencies between the IDE and gradle invocation are resolved soon. Then we’ll be able to fix warnings even before they reach CI!

Happy lint everyone!

--

--