Show us the goods!

Now that you have a brand-spanking-new dependency in your project, you want to jump straight to using this newly found arsenal of annotations.

Let’s take your APIs from zero to hero!

He was a nothin’

A zero, zero

Now he’s a hot shot

He’s a hero

Nullability

There are two nullability annotations, our dearest @NonNull and its counterpart @Nullable. They can be applied to fields, parameters and methods (in which case they refer to the return value).

What they do is pretty obvious: @NonNull means that that symbol is expected to never be null, whereas @Nullable means that you should guard against null values for it. Note that the @Nullable annotation should only be used if that symbol can be null in normal conditions, not if it can be null if misused.

Android Studio uses them as hint for its static analysis tools and the Infer nullity routine.

Resources

When you pass around a resource ID, it looks like a normal integer value. There’s no way of telling if you’re passing, say, a string ID to a method that expects a colour. This is, unless you use the resources annotations.

Using these annotations you can explicitly state that an int value is supposed to be any resource ID (@AnyRes) or a specific resource ID (@*Res), and the tools can check the contract at compile time.

There are a bunch of resources annotations, one for each type of resource: @AnimatorRes, @AnimRes, @AnyRes, @ArrayRes, @AttrRes, @BoolRes, @ColorRes, @DimenRes, @DrawableRes, @FractionRes, @IdRes, @IntegerRes, @InterpolatorRes, @LayoutRes, @MenuRes, @PluralsRes, @RawRes, @StringRes, @StyleableRes, @StyleRes, @XmlRes.

Values

Similar to the way you can mark integers to be resource IDs, you can also specify that they are representing a colour (with the @ColorInt annotation), or that their values must fall within a certain range.

To specify a range for an integer, you can use @IntRange; for a float, its counterpart @FloatRange. You can even specify the expected size of arrays using the @Size annotation.

TypeDefs

As you probably already noticed, the Android framework prefers integer flags to enums. This is done to save memory, as enum values are object instances and take up way more memory than a simple int.

On the other hand, you lose any kind of type safety and check over which kind of values you can bundle up together.

You can use the @interface and @IntDef (or @StringDef, if you want to use strings instead) annotations to define a “virtual enum” annotation that you can then use to mark int symbols as if they were an enum.

Refer to the Creating Enumerated Annotations writeup on the Android Developers website for further details on how to implement TypeDefs. Please note that by default these annotated types are assumed to only assume one of the allowed types. If you want to be able to combine more @IntDef values together, you need to set the flag property to true for the @IntDef.

Proguard

In the future you’ll be able to auto-generate Proguard rules by simply annotating symbols and classes with the @Keep annotation.

At the time of writing the feature is still being developed, because the Android Gradle plugin is not picking those annotations up yet.

Thread safety

There are annotations that you can use to mark some method (or whole class) that need to be run on a specific thread: @UiThread, @MainThread, @WorkerThread, @BinderThread.

The difference between @MainThread and @UiThread is that an app has only one main thread, which is also a UI thread, but can spawn multiple UI threads (one per window). Usually you want to tie lifecycle events to the main thread, and all UI logic to a UI thread.

Bits and bobs

In this section we’ll quickly look at the rest of the annotations in the package, that can’t be easily split into categories.

This set of annotations is what I call architecture annotations. They’re defining a contract that your code should respect when calling into some methods, or just providing some context on some aspects of a class’ API.

The @CallSuper annotation is making explicit that when you override a method from the superclass, you have to call through to super. This really just makes it obvious and shows a warning, but should be always the case.

The @CheckResult annotation is used to remark the fact that the return value of a method should really not be ignored, and it’s neat as it allows you to specify a suggested usage for the return value that the IDE will show in the warning should this contract be violated. This is how the checkSelfPermission() method is annotated, for example.

Lastly, we have the @VisibleForTesting annotation. This isn’t really used by the IDE in any way, as far as I could tell when looking into it. What this does is explicit the reason why a method, or a class, has a broader scope than strictly needed: you’re using it for testing. While you should always minimise the need for this, it’s good to let other devs know that you didn’t mistakenly leave that method as public instead of package private.