Technical docs‎ > ‎

Lint in Studio 2.3


Lint ships with a number of new features in 2.3.


Baseline Support

Creating a Baseline

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


To use this feature, modify your build.gradle file like this:


android {

   lintOptions {

       baseline file("lint-baseline.xml") // your choice of filename/path here

   }

}


Next run lint -- either from the IDE (via Analyze > Inspect Code…) or from the command line, e.g.


$ ./gradlew lintDebug ... :app:lintDebug Wrote HTML report to file:///app/build/outputs/lint-results-debug.html Wrote XML report to file:///app/build/outputs/lint-results-debug.xml Lint found 6 errors, 8 warnings Wrote XML report to file:///app/lint-baseline.xml Created baseline file /app/lint-baseline.xml Also breaking the build in case this was not intentional. If you deliberately created the baseline file, re-run the build and this time it should succeed without warnings. If not, investigate the baseline path in the lintOptions config or verify that the baseline file has been checked into version control. :app:lintDebug FAILED


This will run lint on the project and record all the current issues in the file listed above ("lint-baseline.xml" in the project directory). The set of current issues is called the "baseline". This is a file you'll probably want to check into version control.


Note that if you only want to add some types of issues to the baseline, not all of them, you can add lines to build.gradle to configure the specific issues you want to include:


android {

   lintOptions {

       check 'NewApi', 'HandlerLeak'

       baseline file("lint-baseline.xml")

   }

}


Remember to delete these after creating the baseline file.

Using a Baseline

From now on, if you add any new warnings to the codebase, lint will only list the newly introduced bugs!


With this set up, you may even want to turn all warnings into errors, and fail the build if there are any errors:


android {

   lintOptions {

       baseline file("lint-baseline.xml")

       checkAllWarnings true

       warningsAsErrors true

       abortOnError true

   }

}

Baseline Warnings

When baselines are in effect, you'll also get a warning (but only with "informational" severity) that tells you that one or more issues were filtered out because they were already listed in the baseline. The purpose of this is to avoid a situation where you forget that you have configured a baseline. Ideally you'll want to go back and fix all the issues at some point!


This warning doesn't just tell you the exact number of errors and warnings that it filtered out, but it also keeps track of issues that weren't reported anymore. This lets you know if you've actually fixed issues, so you can optionally re-create the baseline to prevent the error from sneaking back undetected!


lint-baseline.png


Note also that while baselines are enabled when you run Inspections in batch mode in the IDE, they are ignored for the in-editor checks that run in the background when you're editing a file. The purpose is the same: baselines are intended for the case where a codebase has a massive number of existing warnings, but you do want to fix issues locally as you're touching code.

Temporarily Disabling Baseline

In the IDE, the above warning which describes filtered out issues has an associated quickfix which lets you re-run analysis without the baseline. This gives you a quick way to view the actual issues in the codebase and to fix them.


Similarly, the warning which tells you that some issues listed in the baseline appear to be fixed has an IDE quickfix which lets you update the baseline to remove the fixed issues. This helps you ensure that the issues don't creep back in undetected.


New Annotations

The new version of the support library (25.x) shipped with some new and updated annotations that lint can use.

@RestrictTo

This annotation, which can be specified not just on methods and classes but on packages as well, lets you designate an API as restricted. There are several types of restrictions:


  • Subclasses Only classes that are actually extending this class are allowed to access this API. While java has the "protected" modifier, that modifier also allows accesses from unrelated classes within the same package. And there cases where you want to leave a method public for future flexibility (since you can never make a previously protected overridden method public) but want to give a hint that it's intended only for usages within the class or from subclasses. The annotation to use is @RestrictTo(RestrictTo.Scope.SUBCLASSES).

  • Libraries. This allows you to designate that only code within your library can access this code. This allows you to not only organize your code in whatever package hierarchy you want; you can even share code among a group of related libraries. This is already done for the support libraries. They had a lot of implementation code that was not meant for external consumption, but had to be public to be shared across the various complementary support libraries. These classes and packages have now been annotated with @RestrictTo(GROUP_ID) which means that if you accidentally use these implementation classes, lint will warn you that this is discouraged. (In the future we'll also filter these things out from code completion). The annotation to use is @RestrictTo(RestrictTo.Scope.GROUP_ID).
    lint-restricted-group.png

  • Tests. This allows you to designate that only tests are allowed to call this code. This allows you to prevent the scenario where you add some methods intended to help testing, and then other developers start using these methods as well. The annotation to use is @RestrictTo(RestrictTo.Scope.TESTS).

@VisibleForTesting

This annotation is not new, but it now has an optional argument: "otherwise". This lets you designate what the visibility of the method should have been if not for the need to make it visible for testing.


Lint uses this to enforce the intended visibility. For example, if you have a method that you wanted to be private except it's now package private for tests, like this:


@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)

void myMethod() { }


Then lint will complain if anyone calls this from outside the context allowed by private access -- e.g. from a different compilation unit.

lint-restricted-tests.png

You can also specify @VisibleForTesting(otherwise = VisibleForTesting.NONE) to indicate that a method is there only for testing. That's equivalent to using @RestrictTo(TESTS) as described above, and the same lint check is performed.

New Lint Checks

Android Studio 2.3 also ships with a cluster of new lint checks. (There are also a large number of bug fixes to the existing checks -- typically fixing false positives or false negatives.)


Many of these look for specific problems that you'll only need to know about it if it finds the problem in your project -- but here are some notable ones:

Obsolete SDK_INT Checks

Lint now flags useless version checks. Let's say your minSdkVersion used to be 10, and you had some conditional code like this:


if (Build.VERSION.SDK_INT >= 14) {

   codeRequiresApi14();
}


If you later go and change the minSdkVersion to 14 or higher, this check will always be true and is unnecessary. Lint will flag these -- and in Android Studio there's a quickfix to handle it (which in the above case will remove the if; if the logic had been reversed (e.g. Build.VERSION.SDK_INT < 14) it would have removed the code instead.

lint-obsolete-sdk.png

Object Animator Validation

When using an ObjectAnimator, you are referencing methods on a class by name. If you have a typo, the app will compile but crash at runtime. And more subtly, even if you're using the right name and your app works correctly, it's possible that when you build a release version of your app and the app is obfuscated with ProGuard, ProGuard doesn't know that you're referencing this method by name, so it may be renamed and even deleted as unused, which will also cause a crash.


Lint now analyzes your code to make sure that your ObjectAnimator calls are referencing valid methods with the right signatures:

lint-obj-animator-static.png

It also checks that those methods have been annotated with @Keep, which will keep ProGuard from renaming or removing them during release builds. Note that it only warns about missing @Keep annotation on ObjectAnimated properties if your Gradle project is using a shrinker like ProGuard (e.g. has minifyEnabled true.)

lint-animator-keep.png

Unnecessary Item Decorator Copy

Older versions of the RecyclerView library did not include a divider decorator, but one was provided as a sample in the support demos. This divider  class has been widely copy/pasted into various projects.


In recent versions of the support library, the divider decorator is now  included, so you can replace custom copies with the "built-in" version, android.support.v7.widget.DividerItemDecoration.


Lint looks for code that looks like a copy of the old sample and suggests replacing it.

WifiManager Leak

On versions prior to Android N (24), initializing theWifiManager via Context#getSystemService can cause a memory leak if the context is not  the application context. Lint looks for these types of initializations and if it's not certain that a context is the application context, it suggests you change it to context.getApplicationContext().getSystemService(...).

Improved Resource Prefix

Lint has had a resourcePrefix check for quite a while, but it had many limitations. In 2.3 it's quite a bit smarter so you can now configure your project with a prefix, such as this:


android {

   resourcePrefix 'my_lib_'

}


and lint will now make sure that all your resources are using this prefix. Unlike the past, it now lets you use variations of the name for styles and themes, so for example for the above my_lib_ prefix you can have themes named MyLibTheme, myLibAttr, my_lib_layout, and so forth.

And Many More:

  • A lint check which looks for webp images and ensures that if the images use the lossless or transparent image format versions, minSdkVersion is at least 18 (or 15 for the lossy format)

  • There's a new version check which ensures that all support libraries are using the same exact version number. This is similar to the play services libraries, which must also be synchronized to the same version. There's another lint check which looks for the bundled GMS dependency and suggests replacing these with just the necessary individual libraries.

  • Additional version checks around the wearable shared library

  • A lint check for the android:extractNativeLibs manifest attribute, which looks for System.loadLibrary or Runtime.loadLibrary calls and if so suggests setting extractNativeLibs="false" in the manifest

  • A check for apostrophes in XML file which should be escaped

  • A check for high version codes (using version codes which are close to the limit is disallowed by Google Play since the apps cannot be updated.)

  • A number of checks for usage of Firebase APIs, such as using getToken() without having an onTokenRefresh callback, validating analytics event parameter names,

  • A lint check which warns about combining the all-caps style with a string that contains markup

  • A lint check which validates the network-security-config, warning about soon to expire expiration dates, warning if no backup <pin> elements are declared, etc.

  • A lint check which ensures that permissions are declared in the right place



Comments