Cómo mejorar la inspección de código con anotaciones

Las herramientas de inspección de código como lint pueden ayudarte a detectar problemas y mejorar tu código, pero no tienen demasiada capacidad para realizar inferencias. Los IDs de recursos de Android, por ejemplo, usan un elemento int para identificar cadenas, gráficos, colores y otros tipos de recursos, por lo cual las herramientas de inspección no pueden detectar si, en lugar de especificar un recurso de cadenas, deberías especificar un color. Esta situación puede hacer que no se procese correctamente o no se ejecute tu app, incluso si usas la inspección de código.

Las anotaciones te permiten proporcionar indicadores para las herramientas de inspección de código como lint para ayudar a que se detecten estos problemas de código más sutiles. Se agregan como etiquetas de metadatos que adjuntas a valores que se muestran, variables y parámetros para inspeccionar los valores que se muestran de un método, los parámetros pasados, las variables locales y los campos. Cuando se usan con las herramientas de inspección de código, las anotaciones pueden ayudarte a detectar problemas, como excepciones de puntero nulo y conflictos de tipos de recursos.

Android admite diferentes anotaciones por medio de la biblioteca de Jetpack Annotations. Puedes acceder a ella a través del paquete androidx.annotation.

Nota: Si un módulo tiene una dependencia en un procesador de anotaciones, debes usar la configuración de dependencia kapt o ksp para Kotlin, o bien la configuración de dependencia annotationProcessor para Java para agregar esa dependencia.

Cómo agregar anotaciones a tu proyecto

Para habilitar las anotaciones en tu proyecto, agrega la dependencia androidx.annotation:annotation a tu biblioteca o app. Se verificará cualquier anotación que agregues cuando ejecutes una inspección de código o una tarea lint.

Cómo agregar la dependencia de la biblioteca de Jetpack Annotations

La biblioteca de Jetpack Annotations se publica en el repositorio de Maven de Google. Para agregar la biblioteca de Jetpack Annotations a tu proyecto, incluye la siguiente línea en el bloque dependencies de tu archivo build.gradle o build.gradle.kts:

Kotlin

dependencies {
    implementation("androidx.annotation:annotation:1.7.1")
}

Groovy

dependencies {
    implementation 'androidx.annotation:annotation:1.7.1'
}
En la barra de herramientas o en la notificación de sincronización que aparezca, haz clic en Sync Now.

Si usas anotaciones en tu propio módulo de biblioteca, estas se incluyen como parte del artefacto Android ARchive (AAR) en formato XML, en el archivo annotations.zip. Agregar la dependencia androidx.annotation no introduce una dependencia para usuarios de downstream de tu biblioteca.

Nota: Si usas otras bibliotecas de Jetpack, es posible que no necesites agregar la dependencia androidx.annotation. Debido a que muchas otras bibliotecas de Jetpack dependen de la biblioteca de Annotations, es posible que ya tengas acceso a las anotaciones.

Para obtener una lista completa de las anotaciones incluidas en el repositorio de Jetpack, consulta la referencia de la biblioteca de Jetpack Annotations o utiliza la función de autocompletado para mostrar las opciones disponibles para la sentencia import androidx.annotation..

Cómo ejecutar inspecciones de código

Para iniciar una inspección de código desde Android Studio, lo que incluye la validación de anotaciones y la revisión automática de lint, selecciona Analyze > Inspect Code en la barra del menú. Android Studio muestra mensajes de conflicto para marcar posibles problemas entre el código y las anotaciones, y sugerir posibles soluciones.

También puedes ejecutar la tarea lint con la línea de comandos para aplicar anotaciones. Aunque este método puede resultar útil para marcar problemas con un servidor de integración continua, la tarea lint no permite implementar anotaciones de nulidad (que se describen en la siguiente sección), sino que solo Android Studio lo permite. Si quieres obtener más información para habilitar y ejecutar inspecciones de lint, consulta el artículo Cómo mejorar tu código con verificaciones de lint.

Si bien los conflictos de anotaciones generan advertencias, estas no impiden que tu app realice compilaciones.

Anotaciones de nulidad

Las anotaciones de nulidad pueden ser útiles en código Java para determinar si los valores pueden ser nulos. Son menos útiles en código Kotlin, ya que este tiene reglas de nulidad integradas que se aplican en el momento de la compilación.

Agrega las anotaciones @Nullable y @NonNull para verificar la nulidad de un valor que se muestra, una variable o un parámetro determinados. La anotación @Nullable indica que una variable, un parámetro o un valor que se muestra pueden ser nulos. @NonNull indica una variable, un parámetro o un valor que se muestra que no puede ser nulo.

Por ejemplo, si una variable local que contiene un valor nulo se pasa como parámetro a un método con la anotación @NonNull adjunta a ese parámetro, se genera una advertencia durante la compilación del código con la que se indica un conflicto de no nulidad. Además, si intentas hacer referencia al resultado de un método marcado por @Nullable sin verificar primero si el resultado es nulo, se genera una advertencia de nulidad. Solo debes usar @Nullable en el valor que se muestra de un método en caso de que se deba verificar explícitamente la nulidad de cada uso del método.

En el siguiente ejemplo, se muestra la nulabilidad en acción. En el código de ejemplo de Kotlin, no se aprovecha la anotación @NonNull porque se agrega automáticamente al código de bytes generado cuando se especifica un tipo no anulable. En el ejemplo de Java, se aprovecha la anotación @NonNull en los parámetros context y attrs para verificar que los valores del parámetro que se pasan no sean nulos. Además, se verifica que con el método onCreateView() no se muestre un resultado nulo.

Kotlin

...
    /** Annotation not used because of the safe-call operator(?)**/
    override fun onCreateView(
            name: String?,
            context: Context,
            attrs: AttributeSet
    ): View? {
        ...
    }
...

Java

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

Análisis de nulidad

Android Studio admite la ejecución de un análisis de nulabilidad para realizar inferencias e insertar anotaciones de nulidad en tu código automáticamente. Un análisis de nulabilidad analiza los contratos en las jerarquías de los métodos de tu código para detectar lo siguiente:

  • Métodos de llamada que pueden mostrar un valor nulo
  • Métodos que no deben mostrar un valor nulo.
  • Variables, como campos, variables locales y parámetros, que pueden tener un valor nulo
  • Variables, como campos, variables locales y parámetros, que no pueden tener un valor nulo

Luego, el análisis inserta automáticamente las anotaciones de valor nulo correspondientes en las ubicaciones detectadas.

Para ejecutar un análisis de nulabilidad en Android Studio, selecciona Analyze > Infer Nullity. Android Studio inserta las anotaciones @Nullable y @NonNull de Android en las ubicaciones detectadas de tu código. Después de ejecutar un análisis de nulabilidad, recomendamos que verifiques las anotaciones insertadas.

Nota: Si se agregan anotaciones de nulidad, es posible que la función de autocompletado sugiera las anotaciones @Nullable y @NotNull de IntelliJ, en lugar de las anotaciones de valor nulo de Android, y que importe automáticamente la biblioteca correspondiente. Sin embargo, el revisor lint de Android Studio solo detecta las anotaciones de valor nulo de Android. Cuando verifiques tus anotaciones, confirma que en el proyecto se usen las anotaciones de valor nulo de Android, de modo que el revisor lint pueda enviarte notificaciones apropiadas durante la inspección del código.

Anotaciones de recursos

La validación de tipos de recursos puede resultar útil porque las referencias a recursos de Android, como drawable y cadena, se pasan como valores enteros.

Un código para el cual se espera que un parámetro haga referencia a un tipo de recurso específico, como String, puede recibir el tipo de referencia int esperado, pero, en realidad, puede hacer referencia a un tipo de recurso diferente, como R.string.

Por ejemplo, agrega anotaciones @StringRes para verificar si un parámetro de recurso contiene una referencia R.string, como se muestra a continuación:

Kotlin

abstract fun setTitle(@StringRes resId: Int)

Java

public abstract void setTitle(@StringRes int resId)

Durante la inspección de código, la anotación genera una advertencia si no se pasa una referencia R.string en el parámetro.

Se pueden agregar anotaciones para los otros tipos de recursos, como @DrawableRes, @DimenRes, @ColorRes y @InterpolatorRes con el mismo formato de anotación. Además, estas se pueden ejecutar durante la inspección de código.

Si tu parámetro admite varios tipos de recursos, puedes incluir más de una de estas anotaciones en un parámetro determinado. Usa @AnyRes para indicar que el parámetro con anotaciones puede ser cualquier tipo de recurso R.

Si bien puedes usar @ColorRes para especificar que un parámetro debe ser un recurso de color, no se reconoce un valor entero de color (en el formato RRGGBB o AARRGGBB) como recurso de color. En su lugar, usa la anotación @ColorInt para indicar que un parámetro debe ser un valor entero de color. Las herramientas de compilación marcarán el código incorrecto que pase un ID de recurso de color como android.R.color.black, en lugar de un valor entero de color, a métodos con anotaciones.

Anotaciones de subprocesos

Con estas anotaciones, se verifica si se llama a un método desde un tipo específico de subproceso. Se admiten las siguientes anotaciones de subprocesos:

Las herramientas de compilación consideran las anotaciones @MainThread y @UiThread como intercambiables, por lo que puedes llamar a los métodos @UiThread desde los métodos @MainThread, y viceversa. Sin embargo, es posible que un subproceso de IU sea diferente del subproceso principal en el caso de las apps de sistema que tienen varias vistas en distintos subprocesos. Por lo tanto, debes anotar los métodos asociados con la jerarquía de vistas de una app mediante @UiThread y, con @MainThread, solo los métodos vinculados al ciclo de vida de una app.

Si todos los métodos de una clase comparten el mismo requisito de subproceso, puedes agregar una anotación de subproceso único a la clase para verificar que se llame a todos los métodos de esta última desde el mismo tipo de subproceso.

Un uso común de las anotaciones de subprocesos es validar que los métodos o las clases con anotaciones @WorkerThread solo se llamen desde un subproceso en segundo plano adecuado.

Anotaciones de restricción de valor

Usa las anotaciones @IntRange, @FloatRange y @Size para validar los valores de los parámetros pasados. Tanto @IntRange como @FloatRange resultan muy útiles cuando se aplican a parámetros para los cuales es probable que los usuarios obtengan el rango incorrecto.

La anotación @IntRange valida el posicionamiento de un valor entero o un valor del parámetro dentro de un rango especificado. En el siguiente ejemplo, se indica que el parámetro alpha debe contener un valor entero de 0 a 255:

Kotlin

fun setAlpha(@IntRange(from = 0, to = 255) alpha: Int) { ... }

Java

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

La anotación @FloatRange permite verificar que un valor del parámetro doble o flotante se encuentre dentro de un rango especificado de valores de punto flotante. En el siguiente ejemplo, se indica que el parámetro alpha debe contener un valor flotante de 0.0 a 1.0:

Kotlin

fun setAlpha(@FloatRange(from = 0.0, to = 1.0) alpha: Float) {...}

Java

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

La anotación @Size permite verificar el tamaño de una colección o un array, o la longitud de una cadena. La anotación @Size puede usarse para verificar los siguientes atributos:

  • Tamaño mínimo (por ejemplo, @Size(min=2))
  • Tamaño máximo (por ejemplo, @Size(max=2))
  • Tamaño exacto (por ejemplo, @Size(2))
  • Un número que debe ser un divisor del tamaño, como @Size(multiple=2)

Por ejemplo, @Size(min=1) permite verificar si una colección no está vacía, y @Size(3) permite validar que un array contenga exactamente tres valores.

En el siguiente ejemplo, se indica que el array location debe contener al menos un elemento:

Kotlin

fun getLocation(button: View, @Size(min=1) location: IntArray) {
    button.getLocationOnScreen(location)
}

Java

void getLocation(View button, @Size(min=1) int[] location) {
    button.getLocationOnScreen(location);
}

Anotaciones de permisos

Usa la anotación @RequiresPermission para validar los permisos del llamador de un método. Para verificar un solo permiso de una lista de permisos válidos, usa el atributo anyOf. Para verificar un conjunto de permisos, usa el atributo allOf. En el siguiente ejemplo, se usan anotaciones en el método setWallpaper() para indicar que el llamador del método debe tener el permiso permission.SET_WALLPAPERS:

Kotlin

@RequiresPermission(Manifest.permission.SET_WALLPAPER)
@Throws(IOException::class)
abstract fun setWallpaper(bitmap: Bitmap)

Java

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

En este ejemplo, se requiere que el llamador del método copyImageFile() tenga acceso de lectura tanto al almacenamiento externo como a los metadatos de ubicación en la imagen copiada:

Kotlin

@RequiresPermission(allOf = [
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.ACCESS_MEDIA_LOCATION
])
fun copyImageFile(dest: String, source: String) {
    ...
}

Java

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

En el caso de los permisos relacionados con intents, coloca el requisito de permiso en el campo de la string que define el nombre de la acción de intent:

Kotlin

@RequiresPermission(android.Manifest.permission.BLUETOOTH)
const val ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE"

Java

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

En el caso de los permisos relacionados con proveedores de contenido que necesitan permisos de lectura y escritura por separado, une cada requisito de permiso en una anotación @RequiresPermission.Read o @RequiresPermission.Write:

Kotlin

@RequiresPermission.Read(RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write(RequiresPermission(WRITE_HISTORY_BOOKMARKS))
val BOOKMARKS_URI = Uri.parse("content://browser/bookmarks")

Java

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

Permisos indirectos

Cuando un permiso dependa del valor específico que se proporciona al parámetro de un método, usa @RequiresPermission en el parámetro, sin enumerar los permisos específicos. Por ejemplo, el método startActivity(Intent) usa un permiso indirecto sobre el intent que se pasa al método:

Kotlin

abstract fun startActivity(@RequiresPermission intent: Intent, bundle: Bundle?)

Java

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

Cuando usas permisos indirectos, las herramientas de compilación realizan un análisis de flujo de datos para verificar si el argumento que se pasó al método tiene alguna anotación @RequiresPermission. Luego, implementan cualquier anotación del parámetro existente en el método. En el ejemplo de startActivity(Intent), las anotaciones de la clase Intent generan las advertencias resultantes sobre los usos no válidos de startActivity(Intent) cuando se pasa un intent al método sin los permisos correspondientes, como se muestra en la Figura 1.

Figura 1: Advertencia que se genera a partir de una anotación de permisos indirectos en el método startActivity(Intent)

Las herramientas de compilación generan la advertencia sobre startActivity(Intent) a partir de la anotación en el nombre de la acción de intent correspondiente en la clase Intent:

Kotlin

@RequiresPermission(Manifest.permission.CALL_PHONE)
const val ACTION_CALL = "android.intent.action.CALL"

Java

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

Si es necesario, puedes reemplazar @RequiresPermission por @RequiresPermission.Read o @RequiresPermission.Write cuando se anota el parámetro de un método. Sin embargo, en el caso de los permisos indirectos, no se debe usar @RequiresPermission junto con las anotaciones de permisos de lectura ni escritura.

Anotaciones de valor de retorno

Usa la anotación @CheckResult para validar el uso efectivo del resultado o del valor que se muestra de un método. En lugar de anotar cada método no vacío con @CheckResult, agrega la anotación para aclarar los resultados de métodos posiblemente confusos.

Por ejemplo, los nuevos desarrolladores de Java a menudo cometen el error de pensar que <String>.trim() permite quitar los espacios en blanco de la cadena original. La anotación del método con @CheckResult permite marcar los usos de <String>.trim() con los cuales el llamador no realiza ninguna acción con el valor que se muestra del método.

En el siguiente ejemplo, se usan anotaciones en el método checkPermissions() para garantizar que se haga referencia al valor de retorno del método. Además, se designa el método enforcePermission() como método de reemplazo sugerido al desarrollador:

Kotlin

@CheckResult(suggest = "#enforcePermission(String,int,int,String)")
abstract fun checkPermission(permission: String, pid: Int, uid: Int): Int

Java

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

Anotaciones CallSuper

Usa la anotación @CallSuper para validar que un método predominante llame a la superimplementación del método.

En el siguiente ejemplo, se realiza una anotación en el método onCreate() para garantizar que cualquier implementación del método predominante llame a super.onCreate():

Kotlin

@CallSuper
override fun onCreate(savedInstanceState: Bundle?) {
}

Java

@CallSuper
protected void onCreate(Bundle savedInstanceState) {
}

Anotaciones typedef

Las anotaciones typedef permiten garantizar que un parámetro, valor que se muestra o campo en particular haga referencia a un conjunto específico de constantes. Además, permiten que se complete el código para ofrecer automáticamente las constantes permitidas.

Usa las anotaciones @IntDef y @StringDef para crear anotaciones enumeradas de conjuntos de números enteros y cadenas para validar otros tipos de referencias de código.

En este tipo de anotaciones, se usa @interface para declarar el nuevo tipo de anotación enumerada. Las anotaciones @IntDef y @StringDef, junto con @Retention, permiten crear la nueva anotación, y son necesarias para definir el tipo enumerado. La anotación @Retention(RetentionPolicy.SOURCE) indica al compilador que no debe almacenar los datos de la anotación enumerada en el archivo .class.

En el siguiente ejemplo, se muestran los pasos para crear una anotación que garantice que un valor pasado como parámetro del método haga referencia a una de las constantes definidas:

Kotlin

import androidx.annotation.IntDef
//...
// Define the list of accepted constants and declare the NavigationMode annotation.
@Retention(AnnotationRetention.SOURCE)
@IntDef(NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS)
annotation class NavigationMode

// Declare the constants.
const val NAVIGATION_MODE_STANDARD = 0
const val NAVIGATION_MODE_LIST = 1
const val NAVIGATION_MODE_TABS = 2

abstract class ActionBar {

    // Decorate the target methods with the annotation.
    // Attach the annotation.
    @get:NavigationMode
    @setparam:NavigationMode
    abstract var navigationMode: Int

}

Java

import androidx.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);
}

Cuando compilas este código, se genera una advertencia si el parámetro mode no hace referencia a una de las constantes definidas (NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST o NAVIGATION_MODE_TABS).

Combina @IntDef y @IntRange para indicar que un valor entero puede ser un conjunto determinado de constantes o un valor dentro de un rango.

Cómo habilitar la combinación de constantes con marcas

Si los usuarios pueden combinar las constantes permitidas con una marca (por ejemplo, |, &, ^, etc.), puedes definir una anotación con un atributo flag para verificar si un parámetro o valor que se muestra hace referencia a un patrón válido.

En el siguiente ejemplo, se crea la anotación DisplayOptions con una lista de constantes DISPLAY_ válidas:

Kotlin

import androidx.annotation.IntDef
...

@IntDef(flag = true, value = [
    DISPLAY_USE_LOGO,
    DISPLAY_SHOW_HOME,
    DISPLAY_HOME_AS_UP,
    DISPLAY_SHOW_TITLE,
    DISPLAY_SHOW_CUSTOM
])
@Retention(AnnotationRetention.SOURCE)
annotation class DisplayOptions
...

Java

import androidx.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 {}

...

Cuando compilas código con una marca de anotación, se genera una advertencia si el valor que se muestra o el parámetro decorado no hace referencia a un patrón válido.

Cómo mantener la anotación

La anotación @Keep permite garantizar que no se quite una clase o un método con anotaciones cuando se reduzca el código durante el tiempo de compilación. Por lo general, esa anotación se agrega a métodos y clases a los que se accede mediante reflexión para evitar que el compilador la considere código no usado.

Precaución: Las clases y los métodos que anotas con @Keep siempre aparecen en el APK de la app, aunque nunca hayas hecho una referencia a ellos en la lógica de la app.

Para mantener un tamaño pequeño en tu app, considera si es necesario preservar cada anotación @Keep en tu app. Si usas el reflejo para acceder a una clase o un método con anotaciones, usa un condicional -if en tus reglas de ProGuard y especifica la clase que realiza las llamadas de reflexión.

Si quieres obtener más información para reducir tu código y especificar qué código no se debe quitar, consulta Cómo reducir, ofuscar y optimizar tu app.

Anotaciones de visibilidad de código

Usa las siguientes anotaciones para indicar la visibilidad de partes específicas del código, como métodos, clases, campos o paquetes.

Cómo hacer que el código sea visible para la prueba

La anotación @VisibleForTesting indica que un método con anotaciones es más visible de lo necesario para que se puedan hacer pruebas en este. Esa anotación tiene un argumento otherwise opcional que te permite indicar cuál debería haber sido la visibilidad del método si no hubiese sido por la necesidad de que fuera visible para las pruebas. Lint usa el argumento otherwise para aplicar la visibilidad deseada.

En el siguiente ejemplo, myMethod() suele ser private, pero es package-private para las pruebas. Con la designación VisibleForTesting.PRIVATE, lint muestra un mensaje si se llama a ese método desde fuera del contexto permitido por el acceso private, como desde una unidad de compilación diferente.

Kotlin

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun myMethod() {
    ...
}

Java

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
void myMethod() { ... }

También puedes especificar @VisibleForTesting(otherwise = VisibleForTesting.NONE) a fin de indicar que existe un método solo para pruebas. Este formulario equivale al uso de @RestrictTo(TESTS). Ambos recursos realizan la misma verificación de lint.

Cómo restringir una API

La anotación @RestrictTo indica que el acceso a la API con anotaciones (paquete, clase o método) está limitado de la siguiente manera:

Subclases

Usa el formulario de anotación @RestrictTo(RestrictTo.Scope.SUBCLASSES) para que solo las subclases puedan acceder a la API.

Solo las clases que extienden la clase con anotaciones pueden acceder a esta API. El modificador protected de Java no es lo suficientemente restrictivo porque permite el acceso de clases no relacionadas en el mismo paquete. Además, existen casos en los que te convendrá dejar un método como public para tener mayor flexibilidad en el futuro (ya que no es posible que un método previamente protected y anulado se vuelva public) y, luego, indicar que la clase solo está destinada a usos dentro de la clase o desde subclases.

Bibliotecas

Usa el formulario de anotación @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) para que solo tus bibliotecas puedan acceder a la API.

Solo el código de tu biblioteca puede acceder a la API con anotaciones. Eso te permite no solo organizar tu código en la jerarquía de paquetes que desees, sino también compartirlo con un grupo de bibliotecas relacionadas. Esta opción ya está disponible para las bibliotecas de Jetpack con grandes cantidades de código de implementación que no está destinado para uso externo, pero que debe ser public para poder compartirlo entre las diversas bibliotecas de Jetpack complementarias.

Prueba

Usa el formulario de anotación @RestrictTo(RestrictTo.Scope.TESTS) para evitar que otros desarrolladores accedan a tus APIs de prueba.

Solo el código de prueba puede acceder a la API con anotaciones. Esto evita que otros desarrolladores usen APIs que solo tienen fines de prueba.