blob: b979180d2463b226c25d37bb2809533f0ead2924 [file] [log] [blame] [view]
## Annotations {#annotation}
### Annotation processors {#annotation-processor}
Annotation processors should opt-in to incremental annotation processing to
avoid triggering a full recompilation on every client source code change. See
Gradle's
[Incremental annotation processing](https://docs.gradle.org/current/userguide/java_plugin.html#sec:incremental_annotation_processing)
documentation for information on how to opt-in.
### `@RequiresOptIn` APIs {#experimental-api}
Jetpack libraries may choose to annotate API surfaces as unstable using either
Kotlin's
[`@RequiresOptIn` meta-annotation](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-requires-opt-in/)
for APIs written in Kotlin or Jetpack's
[`@RequiresOptIn` meta-annotation](https://developer.android.com/reference/kotlin/androidx/annotation/RequiresOptIn)
for APIs written in Java.
> `@RequiresOptIn` at-a-glance:
>
> * Use for unstable API surfaces
> * Can be called by anyone
> * Documented in public documentation
> * Does not maintain compatibility
For either annotation, API surfaces marked as opt-in are considered alpha and
will be excluded from API compatibility guarantees. Due to the lack of
compatibility guarantees, stable libraries *must never* call experimental APIs
exposed by other libraries outside of their
[same-version group](#same-version-atomic-groups) and *may not* use the `@OptIn`
annotation except in the following cases:
* A library within a same-version group *may* call an experimental API exposed
by another library **within its same-version group**. In this case, API
compatibility guarantees are covered under the same-version group policies
and the library *may* use the `@OptIn` annotation to prevent propagation of
the experimental property. **Library owners must exercise care to ensure
that post-alpha APIs backed by experimental APIs actually meet the release
criteria for post-alpha APIs.**
* An `alpha` library may use experimental APIs from outside its same-version
group. These usages must be removed when the library moves to `beta`.
NOTE JetBrains's own usage of `@RequiresOptIn` in Kotlin language libraries
varies and may indicate binary instability, functional instability, or simply
that an API is really difficult to use. Jetpack libraries should treat instances
of `@RequiresOptIn` in JetBrains libraries as indicating **binary instability**
and avoid using them outside of `alpha`; however, teams are welcome to obtain
written assurance from JetBrains regarding binary stability of specific APIs.
`@RequiresOptIn` APIs that are guaranteed to remain binary compatible *may* be
used in `beta`, but usages must be removed when the library moves to `rc`.
#### When to mark an API surface as experimental
*Do not* use `@RequiresOptIn` for a stable API surface that is difficult to use.
It is not a substitute for a properly-designed API surface.
*Do not* use `@RequiresOptIn` for an API surface that is unreliable or unstable
because it is missing tests. It is not a substitute for a properly-tested API
surface, and all APIs -- including those in `alpha` -- are expected to be
functionally stable.
*Do not* use `@RequiresOptIn` for an internal-facing API surface. Use either the
appropriate language visibility (ex. `private` or `internal`) or `@RestrictTo`.
*Do not* use `@RequiresOptIn` for an API that you expect library developers to
call. Experimental APIs do not maintain binary compatibility guarantees, and you
will put external clients in a difficult situation.
*Do* use `@RequiresOptIn` for API surfaces that must be publicly available and
documented but need the flexibility to stay in `alpha` during the rest of the
library's `beta`, `rc`, or stable cycles, and continue to break compatibility in
`beta`.
#### How to mark an API surface as experimental
All libraries using `@RequiresOptIn` annotations *must* depend on the
`androidx.annotation:annotation-experimental` artifact regardless of whether
they are using the `androidx` or Kotlin annotation. This artifact provides Lint
enforcement of experimental usage restrictions for Kotlin callers as well as
Java (which the Kotlin annotation doesn't handle on its own, since it's a Kotlin
compiler feature). Libraries *may* include the dependency as `api`-type to make
`@OptIn` available to Java clients; however, this will also unnecessarily expose
the `@RequiresOptIn` annotation.
```java
dependencies {
implementation(project(":annotation:annotation-experimental"))
}
```
See Kotlin's
[opt-in requirements documentation](https://kotlinlang.org/docs/reference/opt-in-requirements.html)
for general usage information. If you are writing experimental Java APIs, you
will use the Jetpack
[`@RequiresOptIn` annotation](https://developer.android.com/reference/kotlin/androidx/annotation/RequiresOptIn)
rather than the Kotlin compiler's annotation.
#### How to transition an API out of experimental
When an API surface is ready to transition out of experimental, the annotation
may only be removed during an alpha pre-release stage. Removing the experimental
marker from an API is equivalent to adding the API to the current API surface.
When transitioning an entire feature surface out of experimental, you *should*
remove the definition for the associated experimental marker annotation.
When making any change to the experimental API surface, you *must* run
`./gradlew updateApi` prior to uploading your change.
NOTE Experimental marker annotation *are themselves* experimental, meaning that
it's considered binary compatible to refactor or remove an experimental marker
annotation.
Note: Experimental APIs are reviewed by API Council both when the APIs are first
introduced, and when they are stabilized. API Council may have additional
feedback during stabilization.
### `@RestrictTo` APIs {#restricted-api}
Jetpack's library tooling supports hiding JVM-visible (ex. `public` and
`protected`) APIs from developers using a combination of the `@RestrictTo`
source annotation.
> `@RestrictTo` at-a-glance:
>
> * Use for internal-facing API surfaces
> * Can be called within the specified `Scope`
> * Does not appear in public documentation
> * Does not maintain compatibility in most scopes
While restricted APIs do not appear in documentation and Android Studio will
warn against calling them, hiding an API does *not* provide strong guarantees
about usage:
* There are no runtime restrictions on calling hidden APIs
* Android Studio will not warn if hidden APIs are called using reflection
* Hidden APIs will still show in Android Studio's auto-complete
These annotations indicate that developers should not call an API that is
*technically* public from a JVM visibility perspective. Hiding APIs is often a
sign of a poorly-abstracted API surface, and priority should be given to
creating public, maintainable APIs and using Java visibility modifiers.
*Do not* use `@RestrictTo` to bypass API tracking and review for production
APIs; instead, rely on API+1 and API Council review to ensure APIs are reviewed
on a timely basis.
*Do not* use `@RestrictTo` for implementation detail APIs that are used between
libraries and could reasonably be made public.
*Do* use `@RestrictTo(LIBRARY)` for implementation detail APIs used within a
single library (but prefer Java language `private` or `default` visibility).
#### `RestrictTo.Scope` and inter- versus intra-library API surfaces {#private-api-types}
To maintain binary compatibility between different versions of libraries,
restricted API surfaces that are used between libraries within Jetpack
(inter-library APIs) must follow the same Semantic Versioning rules as public
APIs. Inter-library APIs should be annotated with the
`@RestrictTo(LIBRARY_GROUP)` source annotation.
Restricted API surfaces used within a single library (intra-library APIs), on
the other hand, may be added or removed without any compatibility
considerations. It is safe to assume that developers *never* call these APIs,
even though it is technically feasible. Intra-library APIs should be annotated
with the `@RestrictTo(LIBRARY)` source annotation.
In all cases, correctness and compatibility tracking are handled by AndroidX's
build system and lint checks.
The following table shows the visibility of a hypothetical API within Maven
coordinate `androidx.concurrent:concurrent` when annotated with a variety of
scopes:
<table>
<tr>
<td><code>RestrictTo.Scope</code></td>
<td>Visibility by Maven coordinate</td>
<td>Versioning</td>
<td>Note</td>
</tr>
<tr>
<td><code>LIBRARY</code></td>
<td><code>androidx.concurrent:concurrent</code></td>
<td>No compatibility guarantees (same as private)</td>
<td></td>
</tr>
<tr>
<td><code>LIBRARY_GROUP</code></td>
<td><code>androidx.concurrent:*</code></td>
<td>Semantic versioning (including deprecation)</td>
<td></td>
</tr>
<tr>
<td><code>LIBRARY_GROUP_PREFIX</code></td>
<td><code>androidx.*:*</code></td>
<td>Semantic versioning (including deprecation)</td>
<td></td>
</tr>
<tr>
<td><code>TEST</code></td>
<td><code>*</code></td>
<td>No compatibility guarantees (same as private)</td>
<td>Not allowed in Jetpack, use `@VisibleForTesting`</td>
</tr>
</table>
#### Test APIs and `@VisibleForTesting`
For library APIs that should only be used from test code -- including the
library's own tests, integration test apps, app developers' tests, or
third-party testing libraries -- use the `@VisibleForTesting` annotation to
ensure that the API is only called from test source sets.
Libraries targeted at multi-platform usage in IntelliJ may use `@TestOnly` to
ensure that the IDE enforces usage restrictions.
Jetpack prefers that libraries expose test APIs as public API and maintain
binary compatibility. This ensures that whatever a library needs in its own
integration test app is available to app developers and may be safely called in
third-party testing libraries.
In cases where a test API needs restricted visibility or flexibility around
binary compatibility, the `@VisibleForTesting` annotation may be combined with a
`@RestrictTo` scope or `@RequiresOptIn` feature group.
#### `@IntDef` `@StringDef` and `@LongDef` and visibility
All `@IntDef`, `@StringDef`, and `@LongDef` will be stripped from resulting
artifacts to avoid issues where compiler inlining constants removes information
as to which `@IntDef` defined the value of `1`. The annotations are extracted
and packaged separately to be read by Android Studio and lint which enforces the
types in application code.
* Libraries *must* `@RestrictTo` all `@IntDef`, `@StringDef`, and `@LongDef`
declarations to create a warning when the type is used incorrectly.
* Libraries *must* expose constants used to define the `@IntDef` etc at the
same Java visibility as the hidden `@IntDef`
Here is a complete example of an `@IntDef`
```java
// constants match Java visibility of ExifStreamType
// code outside this module interacting with ExifStreamType uses these constants
public static final int STREAM_TYPE_FULL_IMAGE_DATA = 1;
public static final int STREAM_TYPE_EXIF_DATA_ONLY = 2;
@RestrictTo(RestrictTo.Scope.LIBRARY) // Don't export ExifStreamType outside module
@Retention(RetentionPolicy.SOURCE)
@IntDef({
STREAM_TYPE_FULL_IMAGE_DATA,
STREAM_TYPE_EXIF_DATA_ONLY,
})
public @interface ExifStreamType {}
```
Java visibility should be set as appropriate for the code in question
(`private`, `package`, or `public`) and is unrelated to hiding.
For more, read the section in
[Android API Council Guidelines](https://android.googlesource.com/platform/developers/docs/+/refs/heads/main/api-guidelines/framework.md#framework-hide-typedefs)
#### `*current.txt` File Explanation {#currenttxt}
In this example, `1.3.0-beta02.txt` is just used for an example. This will match
the current library version.
<table>
<tr>
<td><code>api/current.txt</code></td>
<td>All public APIs.</td>
</tr>
<tr>
<td><code>api/1.3.0-beta02.txt</code></td>
<td>All public APIs available in version <code>1.3.0-beta02</code>.
Used to enforce compatibility in later versions. This file is only
generated during Beta.</td>
</tr>
<tr>
<td><code>api/public_plus_experimental_current.txt </code></td>
<td>Superset of all public APIs (<code>api/current.txt</code>) and all
experimental/<code>RequiresOptIn</code> APIs.
</td>
</tr>
<tr>
<td><code>api/public_plus_experimental_1.3.0-beta03.txt</code></td>
<td>Superset of all public APIs (<code>api/1.3.0-beta02.txt.txt</code>) and all
experimental/RequiresOptIn APIs, as available in version
<code>1.3.0-beta02.txt</code>. Only generated during Beta.</td>
<tr>
<td><code>api/restricted_current.txt</code></td>
<td>Superset of all public APIs (<code>api/current.txt</code>) and
all <code>RestrictTo</code> APIs that require compatibility across
versions.
<p/>Specifically, includes <code>@RestrictTo(LIBRARY_GROUP)</code>
(for non-atomically versioned groups) and
<code>@RestrictTo(LIBRARY_GROUP_PREFIX)</code> (for all libraries).</td>
</tr>
<tr>
<td><code>api/restricted_1.3.0-beta02.txt.txt</code></td>
<td>Superset of all public APIs (<code>api/current.txt</code>) and
all <code>RestrictTo</code> APIs that require compatibility across
versions, as available in version <code>1.3.0-beta02.txt</code>.
<p/>
Specifically, includes <code>@RestrictTo(LIBRARY_GROUP)</code>
(for non-atomically versioned groups) and
<code>@RestrictTo(LIBRARY_GROUP_PREFIX)</code> (for all libraries).
This file is only generated during Beta.</td>
</tr>
</table>