Show navigation Hide navigation

Improving Code Inspection with Annotations

Using code inspections tools such as lint can help you find problems and improve your code, but inspection tools can only infer so much. Android resource ids, for example, use an int to identify strings, graphics, colors and other resource types, so inspection tools cannot tell when you have specified a string resource where you should have specified a color. This situation means that your app may render incorrectly or fail to run at all, even if you use code inspection.

Annotations allow you to provide hints to code inspections tools like lint, to help detect these, more subtle code problems. They are added as metadata tags that you attach to variables, parameters, and return values to inspect method return values, passed parameters, and local variables and fields. When used with code inspections tools, annotations can help you detect problems, such as null pointer exceptions and resource type conflicts.

For more information on enabling lint inspections and running lint, see Improving Your Code with lint.

Android supports a variety of annotations for insertion in the methods, parameters, and return values in your code, for example:

@Nullable
Can be null.
@NonNull
Cannot be null.
@StringRes
References a R.string resource.
@DrawableRes
References a Drawable resource.
@ColorRes
References a Color resource.
@InterpolatorRes
References a Interpolator resource.
@AnyRes
References any type of R. resource.

For a complete list of the supported annotations, either examine the contents of the Support-Annotations library or use the auto-complete feature to display the available options for the import android.support.annotation. statement. The SDK Manager packages the Support-Annotations library in the Android Support Repository for use with Android Studio and in the Android Support Library for use with other Android development tools.

To add annotations to your code, first add a dependency to the Support-Annotations library. In Android Studio, add the dependency to your build.gradle file.

dependencies {
    compile 'com.android.support:support-annotations:22.0.0'
}

The Support-Annotations library is decorated with the supported annotations so using this library's methods and resources automatically checks the code for potential problems.

If you include annotations in a library and use the Android Plugin for Gradle to build an Android ARchive (AAR) artifact of that library, the annotations are included as part of the artifact in XML format in the annotations.zip file.

To start a code inspection from Android Studio, which includes validating annotations and automatic lint checking, select Analyze > Inspect Code from the menu options. Android Studio displays conflict messages throughout the code to indication annotation conflicts and suggest possible resolutions.

Adding Nullness Annotations

Add @Nullable and @NonNull annotations to check the nullness of a given variable, parameter, or return value. For example, if a local variable that contains a null value is passed as a parameter to a method with the @NonNull annotation attached to that parameter, building the code generates a warning indicating a non-null conflict.

This example attaches the @NonNull annotation to the context and attrs parameters to check that the passed parameter values are not null.

import android.support.annotation.NonNull;
...
    /** Add support for inflating the <fragment> tag. */
    @NonNull
    @Override
    public View onCreateView(String name, @NonNull Context context,
      @NonNull AttributeSet attrs) {
      ...
      }
...

Note: Android Studio supports running a nullability analysis to automatically infer and insert nullness annotations in your code. For more information about inferring nullability in Android Studio, see Annotations in Android Studio.

Adding Resource Annotations

Add @StringRes annotations to check that a resource parameter contains a R.string reference. During code inspection, the annotation generates a warning if a R.string reference is not passed in the parameter.

Validating resource types can be useful as Android references to Drawables and R.string resources are both passed as integers. Code that expects a parameter to reference a Drawable can be passed the expected reference type of int, but actually reference a R.string resource.

This example attaches the @StringRes annotation to the resId parameter to validate that it is really a string resource.

import android.support.annotation.StringRes;
...
    public abstract void setTitle(@StringRes int resId);
    ...

Annotations for the other resource types, such as @DrawableRes, @ColorRes, and @InterpolatorRes can be added using the same annotation format and run during the code inspection.

Creating Enumerated Annotations

Use the @IntDef and @StringDef annotations so you can create enumerated annotations of integer and string sets to validate other types of code references, such as passing references to a set of constants.

The following example illustrates the steps to create an enumerated annotation that ensures a value passed as a method parameter references one of the defined constants.

import android.support.annotation.IntDef;
...
public abstract class ActionBar {
    ...
    //Define the list of accepted constants
    @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
    //Tell the compiler not to store annotation data in the .class file
    @Retention(RetentionPolicy.SOURCE)
    //Declare the NavigationMode annotation
    public @interface NavigationMode {}
    //Declare the constants
    public static final int NAVIGATION_MODE_STANDARD = 0;
    public static final int NAVIGATION_MODE_LIST = 1;
    public static final int NAVIGATION_MODE_TABS = 2;
    //Decorate the target methods with the annotation
    @NavigationMode
    public abstract int getNavigationMode();
    //Attach the annotation
    public abstract void setNavigationMode(@NavigationMode int mode);

When you build this code, a warning is generated if the mode parameter does not reference one of the defined constants (NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, or NAVIGATION_MODE_TABS).

You can also define an annotation with a flag to check if a parameter or return value references a valid pattern. This example creates the DisplayOptions annotation with a list of valid DISPLAY_ constants.

import android.support.annotation.IntDef;
...
@IntDef(flag=true, value={
        DISPLAY_USE_LOGO,
        DISPLAY_SHOW_HOME,
        DISPLAY_HOME_AS_UP,
        DISPLAY_SHOW_TITLE,
        DISPLAY_SHOW_CUSTOM
})
@Retention(RetentionPolicy.SOURCE)
public @interface DisplayOptions {}
...

When you build code with an annotation flag, a warning is generated if the decorated parameter or return value does not reference a valid pattern.