サイトのトップへ戻る

Android Studio ドキュメント 日本語訳

サイト内検索

アノテーションを使ってコードインスペクションの質を向上させる

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, 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.

Android supports a variety of annotations through the Annotations Support Library. You can access the library through the android.support.annotation package.



Add Annotations to your Project

To enable annotations in your project, add the support-annotations dependency to your library or app. Any annotations you add then get checked when you run a code inspection or lint task.



Add the Support Annotations Library Dependency

The Support Annotations library is part of the Android Support Repository. To add annotations to your project, you must download the support repository and add the support-annotations dependency to your build.gradle file.

  1. Open the SDK Manager by clicking SDK Manager in the toolbar or selecting Tools > Android > SDK Manager.
  2. Click the SDK Tools tab.
  3. Expand Support Repository and check the Android Support Repository checkbox.
  4. Click OK.
  5. Continue through the wizard to install the packages.
  6. Add the support-annotations dependency to your project by putting the following line in the dependencies block of your build.gradle file:
     dependencies { compile 'com.android.support:support-annotations:24.2.0' } 
    The library version you download may be higher, so be sure the value you specify here matches the version from step 3.
  7. In the toolbar or sync notification that appears, click Sync Now.

If you use annotations in your own library module, the annotations are included as part of the Android Archive (AAR) artifact in XML format in the annotations.zip file. Adding the support-annotations dependency does not introduce a dependency for any downstream users of your library.

If you want to use annotations in a Gradle module that is not using the Android Plugin for Gradle (com.android.application or com.android.library) but is using the Gradle Java plugin instead, you must include the SDK repository explicitly because the Android support libraries are not available from the JCenter Java repository:

repositories {
   jcenter()
   maven { url '<your-SDK-path>/extras/android/m2repository' }
}

Note: If you're using the appcompat library, you do not need to add the support-annotations dependency. Because the appcompat library already depends on the annotations library, you have access to the annotations.

For a complete list of annotations included in the support repository, either examine the Support Annotations library reference or use the auto-complete feature to display the available options for the import android.support.annotation. statement.



Run Code Inspections

To start a code inspection from Android Studio, which includes validating annotations and automatic Lint checking, select Analyze > Inspect Code from the menu bar. Android Studio displays conflict messages to flag potential problems where your code conflicts with annotations and to suggest possible resolutions.

You can also enforce annotations by running the lint task using the command line. Although this may be useful for flagging problems with a continuous integration server, note that the lint task does not enforce nullness annotations (only Android Studio does). For more information on enabling and running Lint inspections, see Improving Your Code with Lint.

Note that although annotation conflicts generate warnings, these warnings do not prevent your app from compiling.



Nullness Annotations

Add @Nullable and @NonNull annotations to check the nullness of a given variable, parameter, or return value. The @Nullable annotation indicates a variable, parameter, or return value that can be null while @NonNull indicates a variable, parameter, or return value that cannot be null.

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. On the other hand, attempting to reference the result of a method marked by @Nullable without first checking if the result is null generates a nullness warning. You should only use @Nullable on a method's return value if every use of the method should be explicitly null-checked.

The following example attaches the @NonNull annotation to the context and attrs parameters to check that the passed parameter values are not null. It also checks that the onCreateView() method itself does not return 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) {
      ...
      }
...


Nullability Analysis

Android Studio supports running a nullability analysis to automatically infer and insert nullness annotations in your code. A nullability analysis scans the contracts throughout the method hierarchies in your code to detect:

  • Calling methods that can return null
  • Methods that should not return null
  • Variables, such as fields, local variables, and parameters, that can be null
  • Variables, such as fields, local variables, and parameters, that cannot hold a null value

The analysis then automatically inserts the appropriate null annotations in the detected locations.

To run a nullability analysis in Android Studio, select Analyze > Infer Nullity. Android Studio inserts the Android @Nullable and @NonNull annotations in detected locations in your code. After running a null analysis, it's good practice to verify the injected annotations.

Note: When adding nullness annotations, autocomplete may suggest the IntelliJ @Nullable and @NotNull annotations instead of the Android null annotations and may auto-import the corresponding library. However, the Android Studio Lint checker only looks for the Android null annotations. When verifying your annotations, confirm that your project uses the Android null annotations so the Lint checker can properly notify you during code inspection.



Resource Annotations

Validating resource types can be useful because Android references to resources, such as drawable and string resources, are passed as integers. Code that expects a parameter to reference a specific type of resource, for example Drawables, can be passed the expected reference type of int, but actually reference a different type of resource, such as an R.string resource.

For example, add @StringRes annotations to check that a resource parameter contains an R.string reference, as shown here:

public abstract void setTitle(@StringRes int resId) { … }

During code inspection, the annotation generates a warning if an R.string reference is not passed in the parameter.

Annotations for the other resource types, such as @DrawableRes, @DimenRes, @ColorRes, and @InterpolatorRes can be added using the same annotation format and run during the code inspection. If your parameter supports multiple resource types, you can put more than one of these annotations on a given parameter. Use @AnyRes to indicate that the annotated parameter can be any type of R resource.

Although you can use @ColorRes to specify that a parameter should be a color resource, a color integer (in the RRGGBB or AARRGGBB format) is not recognized as a color resource. Instead, use the @ColorInt annotation to indicate that a parameter must be a color integer. The build tools will flag incorrect code that passes a color resource ID such as android.R.color.black, rather than a color integer, to annotated methods.



Thread Annotations

Thread annotations check if a method is called from a specific type of thread. The following thread annotations are supported:

Note: The build tools treat the @MainThread and @UiThread annotations as interchangeable, so you can call @UiThread methods from @MainThread methods, and vice versa. However, it's possible for a UI thread to be different from the main thread in the case of system apps with multiple views on different threads. Therefore, you should annotate methods associated with an app's view hierarchy with @UiThread and annotate only methods associated with an app's lifecycle with @MainThread.

If all methods in a class share the same threading requirement, you can add a single thread annotation to the class to verify that all methods in the class are called from the same type of thread.

A common use of the thread annotation is to validate method overrides in the AsyncTask class because this class performs background operations and publishes results only on the UI thread.



Value Constraint Annotations

Use the @IntRange, @FloatRange, and @Size annotations to validate the values of passed parameters. Both @IntRange and @FloatRange are most useful when applied to parameters for which users are likely to get the range wrong.

The @IntRange annotation validates that an integer or long parameter value is within a specified range. The following example ensures that the alpha parameter contains an integer value from 0 to 255:

public void setAlpha(@IntRange(from=0,to=255) int alpha) { … }

The @FloatRange annotation checks that a float or double parameter value is within a specified range of floating point values. The following example ensures that the alpha parameter contains a float value from 0.0 to 1.0:

public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {...}

The @Size annotation checks the size of a collection or array, as well as the length of a string. The @Size annotation can be used to verify the following qualities:

  • Minimum size (such as @Size(min=2))
  • Maximum size (such as @Size(max=2))
  • Exact size (such as @Size(2))
  • A number of which the size must be a multiple (such as @Size(multiple=2))
For example, @Size(min=1) checks if a collection is not empty, and @Size(3) validates that an array contains exactly three values. The following example ensures that the location array contains at least one element:

int[] location = new int[3];
button.getLocationOnScreen(@Size(min=1) location);


Permission Annotations

Use the @RequiresPermission annotation to validate the permissions of the caller of a method. To check for a single permission from a list the valid permissions, use the anyOf attribute. To check for a set of permissions, use the allOf attribute. The following example annotates the setWallpaper() method to ensure that the caller of the method has the permission.SET_WALLPAPERS permission:

@RequiresPermission(Manifest.permission.SET_WALLPAPER)
public abstract void setWallpaper(Bitmap bitmap) throws IOException;

This example requires the caller of the copyFile() method to have both read and write permissions to external storage:

@RequiresPermission(allOf = {
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.WRITE_EXTERNAL_STORAGE})
public static final void copyFile(String dest, String source) {
    ...
}

For permissions on intents, place the permission requirement on the string field that defines the intent action name:

@RequiresPermission(android.Manifest.permission.BLUETOOTH)
public static final String ACTION_REQUEST_DISCOVERABLE =
            "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";

For permissions on content providers for which you need separate permissions for read and write access, wrap each permission requirement in an @RequiresPermission.Read or @RequiresPermission.Write annotation:

@RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");


Indirect Permissions

When a permission depends on the specific value supplied to a method's parameter, use @RequiresPermission on the parameter itself, without listing the specific permissions. For example, the startActivity(Intent) method uses an indirect permission on the intent passed to the method:

public abstract void startActivity(@RequiresPermission Intent intent, @Nullable Bundle) {...}

When you use indirect permissions, the build tools perform data flow analysis to check if the argument passed into the method has any @RequiresPermission annotations. They then enforce any existing annotations from the parameter on the method itself. In the startActivity(Intent) example, annotations in the Intent class cause the resulting warnings on invalid uses of startActivity(Intent) when an intent without the appropriate permissions is passed to the method, as shown in figure 1.

Figure 1. The warning generated from an indirect permissions annotation on the startActivity(Intent) method.

The build tools generate the warning on startActivity(Intent) from the annotation on the corresponding intent action name in the Intent class:

@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@RequiresPermission(Manifest.permission.CALL_PHONE)
public static final String ACTION_CALL = "android.intent.action.CALL";

If necessary, you can substitute @RequiresPermission for @RequiresPermission.Read and/or @RequiresPermission.Write when annotating a method's parameter. However, for indirect permissions @RequiresPermission should not be used in conjunction with either of the read or the write permissions annotations.



Return Value Annotations

Use the @CheckResult annotation to validate that a method's result or return value is actually used. Instead of annotating every non-void method with @CheckResult, add the annotation to clarify the results of potentially confusing methods. For example, new Java developers often mistakenly think that <String>.trim() removes whitespace from the original string. Annotating the method with @CheckResult flags uses of <String>.trim() where the caller does not do anything with the method's return value.

The following example annotates the checkPermissions() method to ensure the return value of the method is actually referenced. It also names the enforcePermission() method as a method to be suggested to the developer as a replacement:

@CheckResult(suggest="#enforcePermission(String,int,int,String)")
public abstract int checkPermission(@NonNull String permission, int pid, int uid);


CallSuper Annotations

Use the @CallSuper annotation to validate that an overriding method calls the super implementation of the method. The following example annotates the onCreate() method to ensure that any overriding method implementations call super.onCreate():

@CallSuper
protected void onCreate(Bundle savedInstanceState) {
}


Typedef 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. Typedef annotations ensure that a particular parameter, return value, or field references a specific set of constants. They also enable code completion to automatically offer the allowed constants.

Typedef annotations use @interface to declare the new enumerated annotation type. The @IntDef and @StringDef annotations, along with @Retention, annotate the new annotation and are necessary in order to define the enumerated type. The @Retention(RetentionPolicy.SOURCE) annotation tells the compiler not to store the enumerated annotation data in the .class file.

The following example illustrates the steps to create an 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 and declare the NavigationMode annotation
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
    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 also can combine @IntDef and @IntRange to indicate that an integer can be either a given set of constants or a value within a range.



Enable combining constants with flags

If users can combine the allowed constants with a flag (such as |, &, ^, and so on), you can define an annotation with a flag attribute to check if a parameter or return value references a valid pattern. The following 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.



Code Accessibility Annotations

Use the @VisibleForTesting and @Keep annotations to denote the accessibility of a method, class, or field.

The @VisibleForTesting annotation indicates that a block of code is more visible than necessary in order to make the code testable.

The @Keep annotation ensures that an annotated element is not removed when the code is minified at build time. It is typically added to methods and classes that are accessed via reflection to prevent the compiler from thinking that the code is unused.