Android User Guide

Android User Guide
Contents

(Top)
Lint Command Lint Flags
  1.1  General
  1.2  Enabled Checks
  1.3  Output Options
  1.4  Project Options
  1.5  Advanced Options (for build system integration)
  1.6  Exit Status
Lint Gradle Plugin DSL
  2.1  Configuring Issues and Severity
  2.2  Configuring Output and Reports
  2.3  Files to Include
  2.4  Other
Combining AGP With Newer Lint
  3.1  Configuring the Lint Version
  3.2  Updating Lint
Baselines
Creating a Baseline
  5.1  Customize the baseline
  5.2  Baseline warning
Performance Tuning
  6.1  Use 7.0 and Incremental Lint
  6.2  Use the lintDebug target, not lint
  6.3  Only analyze app/leaf modules
  6.4  Don't analyze test sources
  6.5  Don't use checkAllWarnings
  6.6  Use latest version
  6.7  Give lint a lot of RAM
  6.8  Finding Slow Lint Checks
Suppressing Lint Checks
Configuring Using lint.xml Files
  8.1  XML Syntax
  8.2  Nesting & Precedence
  8.3  Sample lint.xml file
Appendix: Recent Changes
10  Appendix: Environment Variables and System Properties
  10.1  Environment Variables
    10.1.1  Detector Configuration Variables
    10.1.2  Lint Configuration Variables
    10.1.3  Lint Development Variables
  10.2  System Properties

This chapter inlines all the usage information into a single long book, suitable for printing or reading on a tablet.

Recent Changes

This chapter lists recent changes to lint that affect users of lint. For information about internal improvements and API changes affecting lint check authors, see the API Guide.

7.1

Issue ID Summary
FileEndsWithExt Flags suspicious usages of File.endsWith(extension)
DataExtractionRules Missing data extraction rules

Issue ID Summary
MissingTranslation Now checks for missing translations of plurals
WrongConstant Now has quickfixes to replace with constants
CheckResult Now also runs in unit tests

7.0

Issue ID Summary
AnnotateVersionCheck Annotate SDK_INT checks
CoarseFineLocation Cannot use ACCESS_FINE_LOCATION without ACCESS_COARSE_LOCATION
CustomSplashScreen Application-defined Launch Screen
CustomX509TrustManager Implements custom TLS trust manager
HighSamplingRate High sensor sampling rate
IntentFilterExportedReceiver Unspecified android:exported in manifest
LaunchActivityFromNotification Notification Launches Services or BroadcastReceivers
LeanbackUsesWifi Using android.hardware.wifi on TV
MediaCapabilities Media Capabilities property not specified
NotificationTrampoline Notification Trampolines
NotifyDataSetChanged Invalidating All RecyclerView Data
TileProviderPermissions TileProvider does not set permission
TrustAllX509TrustManager Insecure TLS/SSL trust manager
UnspecifiedImmutableFlag Missing PendingIntent mutability flag
WatchFaceEditor Watch face editor must use launchMode=“standard”
WebViewClientOnReceivedSslError Proceeds with the HTTPS connection despite SSL errors

4.2

   

Lint Command Lint Flags

Usage: lint flags

When using lint from within Gradle, see the Lint Gradle Plugin DSL options instead.

   

General

--help

This message.

--help topic

Help on the given topic, such as “suppress”.

--list

List the available issue id's and exit.

--version

Output version information and exit.

--exitcode

Set the exit code to 1 if errors are found.

--show

List available issues along with full explanations.

--show ids

Show full explanations for the given list of issue id's.

--generate-docs

Generates documentation for all the lint checks. This flag cannot be combined with other lint flags, and it has its own sub-flags. Invoke on its own to see what they are.

--fatalOnly

Only check for fatal severity issues

--apply-suggestions

Apply suggestions to the source code (for safe fixes)

--abort-if-suggestions-applied

Set the exit code to an error if any fixes are applied

directories

You can also pass in directories for files to be analyzed. This was common many years ago when project metadata tended to be stored in simple .classpath files from Eclipse; these days, you typically will pass it a --lint-model or --project description instead.

   

Enabled Checks

--disable list

Disable the list of categories or specific issue id's. The list should be a comma-separated list of issue id's or categories.

--enable list

Enable the specific list of issues. This checks all the default issues plus the specifically enabled issues. The list should be a comma-separated list of issue id's or categories.

--check list

Only check the specific list of issues. This will disable everything and re-enable the given list of issues. The list should be a comma-separated list of issue id's or categories.

--fatal list

Sets the default severity of the given issue to fatal

--error list

Sets the default severity of the given issue to error

--warning list

Sets the default severity of the given issue to warning

--info list

Sets the default severity of the given issue to info

-w, —nowarn

Only check for errors (ignore warnings)

-Wall

Check all warnings, including those off by default

-Werror

Treat all warnings as errors

--config filename

Use the given configuration file to determine whether issues are enabled or disabled. If a project contains a lint.xml file, then this config file will be used as a fallback.

--override-config filename

Like —config, but instead of being a fallback, this configuration overrides any local configuration files

--baseline

Use (or create) the given baseline file to filter out known issues.

--update-baseline

Updates the baselines even if they already exist

--remove-fixed

Rewrite the baseline files to remove any issues that have been fixed

--allow-suppress

Whether to allow suppressing issues that have been explicitly registered as not suppressible.

--restrict-suppress

Opposite of —allow-suppress: do not allow suppressing restricted issues

   

Output Options

--quiet

Don't show progress.

--stacktrace

Print full stacktrace for internal errors.

--fullpath

Use full paths in the error output.

--showall

Do not truncate long messages, lists of alternate locations, etc.

--nolines

Do not include the source file lines with errors in the output. By default, the error output includes snippets of source code on the line containing the error, but this flag turns it off.

--html filename

Create an HTML report instead. If the filename is a directory (or a new filename without an extension), lint will create a separate report for each scanned project.

--url filepath=url

Add links to HTML report, replacing local path prefixes with url prefix. The mapping can be a comma-separated list of path prefixes to corresponding URL prefixes, such as C:\temp\Proj1=http://buildserver/sources/temp/Proj1. To turn off linking to files, use —url none

--xml filename

Create an XML report instead.

--sarif filename

Create a SARIF report instead.

--text filename

Write a text report to the given file. If the filename is just stdout (short for standard out), the report is written to the console.

   

Project Options

--resources dir

Add the given folder (or path) as a resource directory for the project. Only valid when running lint on a single project.

--sources dir

Add the given folder (or path) as a source directory for the project. Only valid when running lint on a single project.

--classpath dir

Add the given folder (or jar file, or path) as a class directory for the project. Only valid when running lint on a single project.

--libraries dir

Add the given folder (or jar file, or path) as a class library for the project. Only valid when running lint on a single project.

--compile-sdk-version version

Use the given compileSdkVersion to pick an SDK target to resolve Android API call to

--sdk-home dir

Use the given SDK instead of attempting to find it relative to the lint installation or via $ANDROID_SDK_ROOT

--jdk-home dir

Use the given JDK instead of attempting to find it via $JAVA_HOME or java.home

--java-language-level level

Use the given version of the Java programming language

--kotlin-language-level level

Use the given version of the Kotlin programming language

   

Advanced Options (for build system integration)

--project file

Use the given project layout descriptor file to describe the set of available sources, resources and libraries. Used to drive lint with build systems not natively integrated with lint.

--lint-model path

Alternative to —project which defines the project layout

--variant name

The name of the variant from the lint model to use for analysis

--lint-rule-jars path

One or more .jar files to load additional lint checks from

--analyze-only

Perform only analysis, not reporting, of the given lint model

--report-only

Perform only reporting of previous analysis results

--path-variables variables

Path variables to use in internal persistence files to make lint results cacheable. Use a semi-colon separated list of name=path pairs.

--describe-suggestions file

Describes all the quickfixes in an XML file expressed as document edits — insert, replace, delete

--stacktrace

Dump exceptions from detectors to the console

   

Exit Status

0

Success.

1

Lint errors detected.

2

Lint usage.

3

Cannot clobber existing file.

4

Lint help.

5

Invalid command-line argument.

   

Lint Gradle Plugin DSL

Lint is integrated into three Gradle plugins:

The com.android.lint plugin is the “standalone” lint Gradle plugin which can be applied to any Kotlin or Java Gradle project. To configure this project, use a lint block to configure it, like this:

apply plugin: 'kotlin'
apply plugin: 'com.android.lint'

lint {
    htmlOutput = file("lint-report.html")
    textReport = true
}

The lint options block was called lintOptions until 7.0. You can still refer to it as lintOptions, but this was cleaned up as part of the Android Gradle Plugin's overhaul of its DSL in 7.0. Another 7.0 change is that the lint task no longer runs lint on all variants, but just the default variant — so you no longer have to run :app:lintDebug for example — you can simply run :app:lint.

The com.android.library and com.android.application plugins for Android development bundles in the lint support. They register a lint task for each variant, as well as a task named lint which runs the default variant's lint task.

To configure lint for Android, place the lint block within the android block:

android {
    lint {
        htmlOutput = file("lint-report.html")
        textReport = true
    }
}
   

Configuring Issues and Severity

ignoreWarnings true or false

Whether lint should only report errors. False by default.

checkAllWarnings true or false

Whether lint should check all issues, including those that are marked off by default. False by default.

warningsAsErrors true or false

Whether lint should treat all warnings as errors. False by default.

disable issue-list

List of issue id's that lint should ignore.

Example: disable 'TypographyFractions','TypographyQuotes'.

enable issue-list

List of issues disabled by default that lint should enable.

Example: enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'.

checkOnly issue-list

If any issues are listed as checkOnly, then all other issues are disabled and only those specific issues will be analyzed.

Example: checkOnly 'NewApi', 'InlinedApi'.

fatal issue-list

Sets the severity of the given issues to “fatal” (which means they will be checked during release builds (even if the lint target is not included).

Example: fatal 'NewApi', 'InlineApi'

error issue-list

Sets the severity of the given issues to error.

Example: error 'Wakelock', 'TextViewEdits'

warning issue-list

Sets the severity of the given issues to warning.

Example: warning 'ResourceAsColor'

ignore issue-list

Sets the severity of the given issues to ignore (same as disabling the check).

Example: ignore 'ResourceAsColor'

informational issue-list

Sets the severity of the given issues to informational, which is basically a tip.

Example: informational 'StopShip'

   

Configuring Output and Reports

absolutePaths true or false

Whether lint should include full paths to files in the error reports. If false, paths will be made relative to each module directory. True by default.

noLines true or false

Whether lint should include showing snippets of the source code along with underlines to show the line of code containing the problem. False by default (note the negated name of the flag).

showAll true or false

Whether lint should show all locations (for issues that have multiple locations, such as showing each duplicate declaration of a resources), whether it should not truncate lists, etc.

explainIssues true or false

Whether lint should include a full issue explanations paragraphs in the text error output. Note that this only applies for text reports; HTML and XML reports will always contain explanations. False by default.

textReport true or false

Whether lint should generate a text report. If you don't specify a location using textOutput, lint will default to lint-results-$variant.txt in $module/build/reports/, such as app/build/reports/lint-results-debug.txt`.

textOutput file or 'stdout' or 'stderr'

Where lint should write its text output; either a file, or one of the two magic strings stdout or stderr, which writes to the standard output or standard error streams respectively. In recent versions of lint, this will also imply textReport true.

Example: textOutput file(“$reportsDir/lint-results.txt”)

xmlReport true or false

Whether lint should generate an XML report. If you don't specify a location using xmlOutput, lint will default to lint-results-$variant.xml in $module/build/reports/, such as app/build/reports/lint-results-debug.xml`.

xmlOutput file("$reportsDir/lint-report.xml")

Where lint should write its XML report. In recent versions of lint, this will also imply xmlReport true.

Example: xmlOutput file("$reportsDir/lint-results.xml")

htmlReport true or false

Whether lint should generate an HTML report, with issue explanations, code snippets, etc. If you don't specify a location using htmlOutput, lint will default to lint-results-$variant.html in $module/build/reports/, such as app/build/reports/lint-results-debug.html`.

htmlOutput file

Where lint should write its HTML report;. In recent versions of lint, this will also imply htmlReport true. See also the variables chapter for some flags to configure the HTML report.

Example: htmlOutput file(“$reportsDir/lint-results.html”)

sarifReport true or false

Whether lint should generate a SARIF report for use by for example GitHub. If you don't specify a location using sarifOutput, lint will default to lint-results-$variant.sarif in $module/build/reports/, such as app/build/reports/lint-results-debug.sarif`.

sarifOutput file

Where lint should write its HTML SARIF. In recent versions of lint, this will also imply sarifReport true.

Example: sarifOutput file("$reportsDir/lint-report.sarif.json")

quiet true or false

Whether lint should limit its diagnostic output. False by default.

   

Files to Include

checkGeneratedSources true or false

Normally lint will skip generated sources, but you can turn it on with this flag. False by default.

checkTestSources true or false

Normally most lint checks are not run on test sources (except the checks dedicated to looking for mistakes in unit or instrumentation tests, unless ignoreTestSources is true). You can turn on normal lint checking in all sources with this flag. False by default.

ignoreTestSources true or false

Like checkTestSources, but tells lint to always skip analyzing tests — meaning that it also ignores checks that have explicitly asked to look at test sources, such as the unused resource check. False by default.

checkDependencies true or false

Normally lint will analyze all dependencies along with each module; this ensures that lint can correctly (for example) determine if a resource declared in a library is unused; checking only the library in isolation would not be able to identify this problem. It also gives you a single cumulative report for the entire project, which is easier to digest. However, in older versions of lint this led to performance problems. False by default, but will soon switch to true by default.

   

Other

baseline file

Configures a baseline. The first time lint is run, and the baseline file does not exist, it will record all the current violations into this file (which can be checked into version control). On subsequent runs, only newly introduced issues (not found in the baseline) will be reported.

Example: baseline file("lint-baseline.xml")

lintConfig file

Sets a lint XML file to use as a fallback configuration. Avoid calling this file lint.xml to avoid ambiguity, since those files are automatically loaded.

Example: lintConfig file("default-lint.xml")

abortOnError true or false

If true, stops the Gradle build if one or more errors are found. True by default.

checkReleaseBuilds true or false

If true, include lint analysis in release builds (such as assembleRelease), but limited to issues with severity configured to be fatal, and abort the build if any fatal issues are found (unless abortOnError is set to false). True by default.

The DataFlowAnalyzer received a number of really important fixes in 7.0, so if you are testing with older versions of lint, some of the features described above (such as proper support for Kotlin scoping functions) will not work correctly.

   

Combining AGP With Newer Lint

Lint is integrated with the Android Gradle plugin; when you run lint from the command line or on your CI server, it will first build all the necessary artifacts, which involves running various optional Gradle plugins, and so on.

Updating the Gradle plugin that you're using to build your project is not always possible:

However, lint is in active development, with new checks being added, existing checks being enhanced and extended, and false positive and false negative bugs getting fixed. You'll almost always be better off running the latest version of lint, including preview versions.

As of AGP 7.0, it's possible to use a newer version of lint in conjunction with AGP. The way this works is that you tell AGP which version of lint you want to run, and when you run the lint target, it will first build all the necessary artifacts using the current version of AGP and build scripts. Then it will invoke the newer version of lint, passing in a bunch of state for lints purposes, and the newer lint will then analyze the code and produce its report.

   

Configuring the Lint Version

In gradle.properties, add a property declaration like this:

android.experimental.lint.version = 7.1.0-beta01

This feature flag is experimental and will change once the flag is promoted.

That's all there is to it. Now running ./gradlew :app:lint will build the codebase using your AGP version configured in build.gradle, such as 7.0.0, and then the lint analysis and reporting will use version 7.1.0-beta01.

This will only work with AGP 7.0.0-beta05 later. Note also that while running a more recent version is supported, you cannot run an older version of lint than AGP. This is usually not what you want anyway.

   

Updating Lint

Provided the GradleDependency issue is enabled, lint will check your gradle.properties file and warn you if there is a newer version of lint available than the one you're pointing to:

gradle.properties:2: Warning: Newer version of lint available: 7.1.0-alpha04 [GradleDependency]
android.experimental.lint.version = 7.0.0-rc01
                                    ~~~~~~~~~~~~~
0 errors, 1 warnings

There is also a quickfix in the IDE to perform this update.

Normally, the GradleDependency warning will only suggest updating to a newer version that is a preview if the current dependency is a preview. However, for using the unbundled lint version, lint will always suggest using previews. If you for some reason want to limit yourself to stable versions, such as 8.0.0 while still building with 7.0.0, you can suppress the warning like this:

#noinspection GradleDependency
android.experimental.lint.version=8.0.0
   

Baselines

   

Creating a Baseline

You can take a snapshot of your project's current set of warnings, and then use the snapshot as a baseline for future inspection runs so that only new issues are reported. The baseline snapshot lets you start using lint to fail the build without having to go back and address all existing issues first.

To create a baseline snapshot, modify your project's build.gradle`` file as follows.

android {
  lintOptions {
    baseline file("lint-baseline.xml")
  }
}

When you first add this line, the lint-baseline.xml file is created to establish your baseline. From then on, the tools only read the file to determine the baseline. If you want to create a new baseline, manually delete the file and run lint again to recreate it.

Then, run lint from the IDE (Analyze > Inspect Code) or from the command line as follows. The output prints the location of the lint-baseline.xml file. The file location for your setup might be different from what is shown here.

    $ ./gradlew lintDebug
    ...
    Wrote XML report to file:///app/lint-baseline.xml
    Created baseline file /app/lint-baseline.xml

Running lint records all of the current issues in the lint-baseline.xml file. The set of current issues is called the baseline, and you can check the lint-baseline.xml file into version control if you want to share it with others.

   

Customize the baseline

If you want to add some issue types to the baseline, but not all of them, you can specify the issues to add by editing your project's build.gradle, as follows.

android {
  lintOptions {
    check 'NewApi', 'HandlerLeak'
    baseline file("lint-baseline.xml")
  }
}

After you create the baseline, if you add any new warnings to the codebase, lint lists only the newly introduced bugs.

   

Baseline warning

When baselines are in effect, you get an informational warning that tells you that one or more issues were filtered out because they were already listed in the baseline. The reason for this warning is to help you remember that you have configured a baseline, because ideally, you would want to fix all of the issues at some point.

This informational warning does not only tell you the exact number of errors and warnings that were filtered out, it also keeps track of issues that are not reported anymore. This information lets you know if you have actually fixed issues, so you can optionally re-create the baseline to prevent the error from coming back undetected.

Baselines are enabled when you run inspections in batch

mode in the IDE, but they are ignored for the in-editor checks that run in the background when you are editing a file. The reason is that baselines are intended for the case where a codebase has a massive number of existing warnings, but you do want to fix issues locally while you touch the code.

Official documentation

   

Performance Tuning

Lint tends to get slower for every release. There's a reason for that: It keeps checking more and more things (as of 7.0 canary we're up to around 400 separate issues), and existing issues are often made more complex as well, doing deeper flow and data analysis, and occasionally there are new features which also add costs, such as baselines, Kotlin analysis, etc. And this is all compounded by the codebases lint is getting run on also growing bigger and bigger, and being broken up into more and more modules.

This chapter provides some tips on how to improve the performance of lint on CI servers.

Some of these suggestions are not true in all cases, so you might want to time before and after to verify that for your project each change is an improvement.

   

Use 7.0 and Incremental Lint

In 7.0, lint will finally be capable of running incrementally across modules, meaning that if you change code in just one module, lint will only have to rerun analysis on modules downstream from that module. With large projects with many modules, this should be a significant improvement.

To use this:

  1. Update to the latest 7.0 canary 12 or later.

  2. Update your CI configuration such that it does not clean the working directories between each build.

We are working on making the state files used by this mechanism path relative such that this facility will also work for Gradle's remote cache; once that's done, this will work for clean builds as well.

   

Use the lintDebug target, not lint

If you run “./gradlew tasks” you'll see that there's a lint task, and you may be tempted to run it. But lint also adds specific tasks for each variant. For example, if you only have “debug” and “release” variants (e.g. you haven't defined any product flavors or additional build types), lint will create 3 targets:

If you have defined additional build types or product flavors, you can end up with many more of these — lintFreeDebug, lintProDebug, lintFreeBeta, lintProBeta, etc.

Don't configure your CI job to run the lint task; pick a specific variant instead, such as lintDebug. The reason this is so important, is that what the lint task really does is run lint over and over again, for each variant, and then it collates the results in the end! It combines all the errors it found, and shows which specific variants every error applies to, unless it applies to all (which is nearly always the case).

The rationale for this behavior is that you could have a variant-specific issue; for example, you may have a resource file which is only present in a variant-specific source set (such as src/debug/res/values), so the general lint target checks everything. Of course, you're probably only shipping your release variant, so you could limit yourself to just running lintRelease and you're not going to miss much.

You're probably shipping your release variant, not your debug variant, so instead of running lintDebug it's probably more accurate to run lintRelease. However, unless you're doing anything variant specific, it's unlikely that this matters, and running lintRelease can take significantly longer since it performs more build time optimizations.

If you have lots of variants this makes a huge difference; with 7 variants, lint will take ~7x longer than lintDebug !! (modulo some minor caching)

In 7.0 and Arctic Fox we're changing the behavior of the lint target such that it is just an alias for lintDebug, so once you update to 7.0 this is no longer a problem.

   

Only analyze app/leaf modules

If you have divided your project into many smaller modules - a number of libraries and just a couple of app modules, it's much better to

  1. Turn on checkDependencies mode, and

  2. Only run lint on the app modules, instead of recursively running lint on each module.

To do this, first add this to your app module's build.gradle file:

android {
    ...
    lintOptions {
        ...
        checkDependencies true
        ...
    }
}

Then instead of running ./gradlew lintDebug, run ./gradlew :app:lintDebug.

Since you've turned on check-dependencies mode, running lint on the app module will also run it on all the dependent modules the app depends on — e.g. all the libraries. But this should also make things run a lot faster, since it's a single lint invocation, where lint shares a lot more computation (such as symbol resolution in the SDK and various shared libraries, etc).

Note that checkDependencies true will also cause lint to analyze Kotlin-only or Java-only modules, which the normal ./gradlew lintDebug approach will not do, since that just invokes lint on modules that apply the Android plugins. This is actually a good thing since that code does get included in your Android app and lint has a number of checks that are relevant for that code. But note that this analysis isn't free, so it's technically possible that in some cases (if you have a lot of these modules relative to the number of Android modules) lint will not actually run faster with checkDependencies true. However, for this scenario it's probably a change you want to make anyway.

This isn't just a good idea from a performance perspective; you'll also get more accurate results. For example, the unused resource analysis will now correctly know whether a resource defined in a library is consumed by the app module; that doesn't happen when you run lint first on the library, and later on just the app, which is what happens when you run ./gradlew lintDebug. As a bonus you'll also get a single HTML report containing all the results from your codebase, instead of having to hunt around the tree for all the various reports and look at each one separately.

This mode used to be on by default, since it has better usability. We had to turn it off for performance reasons, because many users were running redundant targets. But in 7.0 we've added a new mechanism (partial analysis) which solves these performance problems, so we will most likely turn this mode back on by default.

   

Don't analyze test sources

Lint has two flags controlling what to do about test sources: checkTestSources and ignoreTestSources. These mean different things.

The checkTestSources option controls whether lint should run all the normal checks on the test sources. This is already off by default, so there's really no performance reason to tweak it. The only reason you'd turn this on is if you really want to make sure your test sources are pristine as well. The ignoreTestSources option on the other hand controls whether lint should really ignore all test sources. Even if lint doesn't run normal checks on the test sources, it still has to include them in analysis for some of the regular lint checks for production code. For example, what if you have a resource which is only referenced from a test; do you then want to flag this as unused when the test is still referencing it?

Add the following to your app module's build.gradle file to tell lint to completely ignore your test sources, which should help lint run faster since (for well tested code) this can help skip a lot of code analysis and type resolution:

android {
    ...
    lintOptions {
        ...
        ignoreTestSources true
        ...
    }
}
   

Don't use checkAllWarnings

Lint has a checkAllWarnings option you can use to turn on checking of all warnings, including those that are off by default. This flag is off by default, but some developers turn it on (“because I want all the tips I can get” is what I heard from one developer).

Be careful doing that, because some of the disabled checks are slow, and some have a lot of false positives which is why they're off by default but available if you really want to audit for one specific problem. (In older versions this would also turn on the WrongThreadInterprocedural check, which is particularly expensive, though in recent versions we've specifically exempted this check from checkAllWarnings.)

   

Use latest version

Try using the latest versions of lint (the Android Gradle plugin), as well as of Gradle — even if that means using a canary version. Yes, by all means, for your production builds use a stable version of the Gradle plugin to build your release APK/bundle, but for your CI server, consider running the latest preview version of lint, since generally those versions will have the most up to date fixes. (Yes, regressions happen and canaries go through less testing, but in general lint has really good coverage so regressions do not happen very often.)

   

Give lint a lot of RAM

This one is kind of obvious, but lint (especially the code analysis part) is really memory hungry; it does a lot of the same work as a compiler, except that it also hangs on to a lot more data (e.g. the full ASTs with whitespace and comments etc). Explore bumping up the memory given to the Gradle daemon significantly to see if it helps bring down the overall analysis time.

This is especially important if you happen to run many lint jobs in parallel! If you follow the advice above (where you're running a single lint job with recursive dependencies on the app module) this is less likely to happen, but I've seen various thread dumps from users asking about performance where there was 16-20 concurrent separate lint jobs running in the Gradle daemon, and each one requires a lot of memory; these separate lint job threads are not sharing data.

One user reported this result:

I have a very large project (with over 2000 classes) and giving lint more memory had a huge effect:

./gradlew -Dorg.gradle.jvmargs=-Xmx2g app:lintDebug takes 25m 18s

./gradlew -Dorg.gradle.jvmargs=-Xmx8g app:lintDebug takes 8m 57s!

   

Finding Slow Lint Checks

We have a tool we use to try to attribute the time spent during analysis to individual lint checks. When analysis is taking longer than expected, we run this tool to see if any of the lint checks are misbehaving. Here's some sample output:

$ ./gradlew lintDebug -no-daemon -Dorg.gradle.jvmargs="..."

BUILD SUCCESSFUL in 10s
15 actionable tasks: 1 executed, 14 up-to-date

Lint detector performance stats:
                                     total           self          calls
                 LintDriver        3709 ms        1302 ms           2821
TopDownAnalyzerFacadeForJVM        2121 ms        2121 ms              6
               IconDetector          81 ms          81 ms            257
           OverdrawDetector          51 ms          51 ms             36
     InvalidPackageDetector          34 ms          34 ms          51744
             GradleDetector          20 ms          20 ms             94
       LocaleFolderDetector          11 ms          11 ms            986
         RestrictToDetector          11 ms          11 ms             19
                ApiDetector          11 ms          11 ms           1255
    PrivateResourceDetector          10 ms          10 ms            422
                      [...]

Here you can see that InvalidPackageDetector is taking up an unreasonable amount of time. And this is actually real output from an earlier version of lint where we tracked down a real problem in that detector.

You can read more about this tool in this post.

If you see a suspicious check, you can try to disable its issues (unless you find their value is worth the cost; after all, real world bugs are typically more expensive than server compute cycles.) But don't forget to also report the bug to the lint check author!

One thing to be aware of is that the “blame” is not entirely fair. There are a number of expensive operations in lint, and especially symbol resolution, which is cached. That means that the first unlucky detector that comes along has to perform the computation, and subsequent detectors just get to reuse the result. Because of this, it's not always the case that a particular detector is a performance culprit, and as soon as you disable it, a new detector moves to the top of the list paying the same initialization costs. The main use for this tool is to find extreme or unusual performance behaviors.

   

Suppressing Lint Checks

Lint errors can be suppressed in a variety of ways:

  1. With a @SuppressLint annotation in the Java code
  2. With a tools:ignore attribute in the XML file
  3. With a //noinspection comment in the source code
  4. With ignore flags specified in the build.gradle file, as explained below
  5. With a lint.xml configuration file in the project
  6. With a lint.xml configuration file passed to lint via the —config flag
  7. With the —ignoreflag passed to lint
  8. With a baseline

To suppress a lint warning with an annotation, add a @SuppressLint("id") annotation on the class, method or variable declaration closest to the warning instance you want to disable. The id can be one or more issue id's, such as "UnusedResources" or {"UnusedResources", "UnusedIds"}, or it can be "all" to suppress all lint warnings in the given scope.

You can also use @Suppress in Kotlin or @SuppressWarnings in Java.

To suppress a lint warning with a comment, add a //noinspection id comment on the line before the statement with the error.

To suppress a lint warning in an XML file, add a tools:ignore="id" attribute on the element containing the error, or one of its surrounding elements. You also need to define the namespace for the tools prefix on the root element in your document, next to the xmlns:android declaration: xmlns:tools="http://schemas.android.com/tools"

To suppress a lint warning in a build.gradle file, add a section like this:

android {
    lintOptions {
        disable 'TypographyFractions','TypographyQuotes'
    }
}

Here we specify a comma separated list of issue id's after the disable command. You can also use warning or error instead of disable to change the severity of issues.

To suppress lint warnings with a configuration XML file, create a file named lint.xml. See the lint.xml configuration document for more details.

To suppress lint checks from the command line, pass the --ignore flag with a comma separated list of ids to be suppressed, such as:

$ lint --ignore UnusedResources,UselessLeaf /my/project/path

For more information, see https://developer.android.com/studio/write/lint.html#config

   

Configuring Using lint.xml Files

In addition to configuring lint with command line flags or Gradle DSL options, you can also create XML files named lint.xml, which lint will look for automatically.

Like .gitignore files, these can be nested, so you can for example create a lint.xml file which sets the severity of an issue to error, but then in a specific subfolder change the severity to be just a warning.

This chapter describes the syntax of lint.xml files.

   

XML Syntax

The root tag is always <lint>, and it can contain one or more <issue> elements. Each can specify the following attributes: id: The issue id the following configuration applies to. Note that this can be a comma separated list of multiple id's, in which case the configuration applies to all of them. It can also be the special value “all”, which will match all issue id's. And when configuring severity, the id is also allowed to be a category, such as “Security”.

in: Specifies that this configuration only applies when lint runs in the given hosts. There are predefined names for various integrations of lint; “gradle” refers to lint running in the Gradle plugin; “studio” refers to lint running in the IDE, “cli” refers to lint running from the command line tools “lint” binary, etc. Like with id's, this can be a comma separated list, which makes the rule match if the lint host is any of the listed hosts. Finally, note that you can also add a “!” in front of each host to negate the check. For example, to enable a check anywhere except when running in Studio, use in="!studio".

In addition, the element can specify one or more children:

<ignore path="...">: Specifies a path to ignore. Can contain the globbing character “*” to match any substring in the path. <ignore regexp="...">: Specifies either a regular expression to ignore. The regular expression is matched against both the location of the error and the error message itself. <option name="..." value="...">: Specifies an option value. This can be used to configure some lint checks with options.

Finally, on the root element you can specify a number of attributes, such as lintJars (a list of jar files containing custom lint checks, separated by a semicolon as a path separator), and flags like warningsAsErrors, checkTestSources, etc (matching most of the flags offered via the lintOptions block in Gradle files.)

   

Nesting & Precedence

You can specify configurations for “all”, but these will be matched after an exact match has been done. E.g. if we have both <issue id="all" severity="ignore"> and <issue id="MyId" severity="error">, the severity for MyId will be “error”“ since that's a more exact match.

The lint.xml files can be nested in a directory structure, and when lint reports an error, it looks up the closest lint.xml file, and if no configuration is found there, continues searching upwards in the directory tree. This means that the configuration closest to the report location takes precedence. Note however, that this has higher priority than the above all versus id match, so if there is an all match in a lint.xml file and an exact match in a more distant parent lint.xml file, the closest lint.xml all match will be used.

When there are configurations which specify a host, lint will search in this order:

  1. An exact host match. E.g. if you're running in Studio and there is an <issue> configuration which specifies in="studio", then that configuration will be used.
  2. A match which does not specify a host. Usually <issue> configurations do not specify a host, and these will be consulted next.
  3. A match which specifies other hosts. For example, if you're running in Studio and a configuration specifies ”!gradle“, this will match after the other attempts.

   

Sample lint.xml file

Typically lint.xml files are pretty short and simple, but here's one which uses most of the available features. (Note: the HTML version of this isn't properly handling empty elements, so if you're going to copy/paste go to the source file.)

<?xml version="1.0" encoding="UTF-8"?>
<lint lintJars="../checks/local.jar;../checks/custom.jar">
    <!-- The special id "all" matches all issues but is only consulted
         if there is no specific match -->
    <issue id="all" severity="ignore" />
    <!-- Possible severities: ignore, information, warning, error, fatal -->
    <issue id="ValidActionsXml" severity="error" />
    <issue id="ObsoleteLayoutParam">
        <!-- The <ignore> tag has two possible attributes: path and regexp (see below) -->
        <ignore path="res/layout-xlarge/activation.xml" />
        <!-- You can use globbing patterns in the path strings -->
        <ignore path="**/layout-x*/onclick.xml" />
        <ignore path="res/**/activation.xml" />
    </issue>
    <issue id="NewApi">
        <!-- You can also ignore via a regular expression, this is not only
            matched against the path but also the error message -->
        <ignore regexp="st.*gs" />
    </issue>
    <!-- The "in" attribute lets you specify that the element only
         applies in a particular tools, such as gradle, studio, etc; this
         can be a comma separated list -->
    <issue in="studio" id="NewerVersionAvailable" severity="error" />
    <!-- You can also use ! to specify that it does not apply in a tool  -->
    <issue in="!gradle" id="TrulyRandom" severity="error" />
    <issue id="UnknownNullness">
        <!-- For detectors that support it, you can also specify option values -->
        <option name="ignore-deprecated" value="true" />
    </issue>
    <issue id="TooManyViews">
        <option name="maxCount" value="20" />
    </issue>
</lint>

   

Appendix: Recent Changes

Recent Changes

This chapter lists recent changes to lint that affect users of lint. For information about internal improvements and API changes affecting lint check authors, see the API Guide.

7.1

Issue ID Summary
FileEndsWithExt Flags suspicious usages of File.endsWith(extension)
DataExtractionRules Missing data extraction rules

Issue ID Summary
MissingTranslation Now checks for missing translations of plurals
WrongConstant Now has quickfixes to replace with constants
CheckResult Now also runs in unit tests

7.0

Issue ID Summary
AnnotateVersionCheck Annotate SDK_INT checks
CoarseFineLocation Cannot use ACCESS_FINE_LOCATION without ACCESS_COARSE_LOCATION
CustomSplashScreen Application-defined Launch Screen
CustomX509TrustManager Implements custom TLS trust manager
HighSamplingRate High sensor sampling rate
IntentFilterExportedReceiver Unspecified android:exported in manifest
LaunchActivityFromNotification Notification Launches Services or BroadcastReceivers
LeanbackUsesWifi Using android.hardware.wifi on TV
MediaCapabilities Media Capabilities property not specified
NotificationTrampoline Notification Trampolines
NotifyDataSetChanged Invalidating All RecyclerView Data
TileProviderPermissions TileProvider does not set permission
TrustAllX509TrustManager Insecure TLS/SSL trust manager
UnspecifiedImmutableFlag Missing PendingIntent mutability flag
WatchFaceEditor Watch face editor must use launchMode=“standard”
WebViewClientOnReceivedSslError Proceeds with the HTTPS connection despite SSL errors

4.2

   

Appendix: Environment Variables and System Properties

This chapter lists the various environment variables and system properties that Lint will look at. None of these are really intended to be used or guaranteed to be supported in the future, but documenting what they are seems useful.

   

Environment Variables

   

Detector Configuration Variables

ANDROID_LINT_INCLUDE_LDPI

Lint's icon checks normally ignore the ldpi density since it's not commonly used any more, but you can turn this back on with this environment variable set to true.

ANDROID_LINT_MAX_VIEW_COUNT

Lint's TooManyViews check makes sure that a single layout does not have more than 80 views. You can set this environment variable to a different number to change the limit.

ANDROID_LINT_MAX_DEPTH

Lint's TooManyViews check makes sure that a single layout does not have a deeper layout hierarchy than 10 levels.You can set this environment variable to a different number to change the limit.

ANDROID_LINT_NULLNESS_IGNORE_DEPRECATED

Lint's UnknownNullness which flags any API element which is not explicitly annotated with nullness annotations, normally skips deprecated elements. Set this environment variable to true to include these as well.

Corresponding system property: lint.nullness.ignore-deprecated

   

Lint Configuration Variables

ANDROID_SDK_ROOT

Locates the Android SDK root

ANDROID_HOME

Locates the Android SDK root, if $ANDROID_SDK_ROOT has not been set

JAVA_HOME

Locates the JDK when lint is analyzing JDK (not Android) projects

LINT_XML_ROOT

Normally the search for lint.xml files proceeds upwards in the directory hierarchy. In the Gradle integration, the search will stop at the root Gradle project, but in other build systems, it can continue up to the root directory. This environment variable sets a path where the search should stop.

ANDROID_LINT_JARS

A path of jar files (using the path separator — semicolon on Windows, colon elsewhere) for lint to load extra lint checks from

ANDROID_SDK_CACHE_DIR

Sets the directory where lint should read and write its cache files. Lint has a number of databases that it caches between invocations, such as its binary representation of the SDK API database, used to look up API levels quickly. In the Gradle integration of lint, this cache directory is set to the root build/ directory, but elsewhere the cache directory is located in a lint subfolder of the normal Android tooling cache directory, such as ~/.android.

LINT_OVERRIDE_CONFIGURATION

Path to a lint XML file which should override any local lint.xml files closer to reported issues. This provides a way to globally change configuration.

Corresponding system property: lint.configuration.override

LINT_DO_NOT_REUSE_UAST_ENV

Set to true to enable a workaround (if affected) for bug 159733104 until 7.0 is released.

Corresponding system property: lint.do.not.reuse.uast.env

LINT_API_DATABASE

Point lint to an alternative API database XML file instead of the normally used $SDK/platforms/android-?/data/api-versions.xml file.

   

Lint Development Variables

LINT_PRINT_STACKTRACE

If set to true, lint will print the full stack traces of any internal exceptions encountered during analysis. This is useful for authors of lint checks, or for power users who can reproduce a bug and want to report it with more details.

Corresponding system property: lint.print-stacktrace

LINT_TEST_KOTLINC

When writing a lint check unit test, when creating a compiled or bytecode test file, lint can generate the .class file binary content automatically if it is pointed to the kotlinc compiler.

LINT_TEST_JAVAC

When writing a lint check unit test, when creating a compiled or bytecode test file, lint can generate the .class file binary content automatically if it is pointed to the javac compiler.

INCLUDE_EXPENSIVE_LINT_TESTS

When working on lint itself, set this environment variable to true some really, really expensive tests that we don't want run on the CI server or by the rest of the development team.

   

System Properties

To set system properties when running lint via Gradle, try for example ./gradlew lintDebug -Dlint.baselines.continue=true

lint.baselines.continue

When you configure a new baseline, lint normally fails the build after creating the baseline. You can set this system property to true to force lint to continue.

lint.autofix

Turns on auto-fixing (applying safe quickfixes) by default. This is a shortcut for invoking the lintFix targets or running the lint command with --apply-suggestions.

lint.html.prefs

This property allows you to customize lint's HTML reports. It consists of a comma separated list of property assignments, e.g. ./gradlew :app:lintDebug -Dlint.html.prefs=theme=darcula,window=5

Property Explanation and Values Default
theme light, darcula, solarized light
window Number of lines around problem 3
maxIncidents Maximum incidents shown per issue type 50
splitLimit Issue count before “More...” button 8
maxPerIssue Name of split limit prior to 7.0 8
underlineErrors If true, wavy underlines, else highlight true

lint.unused-resources.exclude-tests

Whether the unused resource check should exclude test sources as referenced resources.

lint.configuration.override

Alias for $LINT_OVERRIDE_CONFIGURATION

lint.print-stacktrace

Alias for $LINT_PRINT_STACKTRACE

lint.do.not.reuse.uast.env

Alias for $LINT_DO_NOT_REUSE_UAST_ENV

android.lint.log-jar-problems

Controls whether lint will complain about custom check lint jar loading problems. By default, true.

formatted by Markdeep 1.13