Skip to content

Most visited

Recently visited

navigation

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 ID de recursos de Android, por ejemplo, usan un int para identificar strings, 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 string, deberías especificar un color. Esta situación puede hacer que tu app no se represente correctamente o no se ejecute, incluso si usas la inspección de código.

Las anotaciones te permiten proporcionar pistas para las herramientas de inspección de código como Lint, a fin de que se detecten estos problemas de código más sutiles. Se agregan como etiquetas de metadatos que adjuntas a variables, parámetros y valores de retorno para inspeccionar valores de retorno de método, parámetros pasados, variables locales y 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 compatibilidad de anotaciones. Puedes acceder a la biblioteca mediante el paquete android.support.annotation.

Agregar anotaciones a tu proyecto

Para habilitar las anotaciones en tu proyecto, agrega la dependencia support-annotations a tu biblioteca o app. Cualquier anotación que agregues luego se verificará cuando ejecutes una inspección de código o una tarea lint.

Agregar la dependencia de biblioteca de anotaciones de compatibilidad

La biblioteca de anotaciones de compatibilidad forma parte del repositorio de compatibilidad de Android. Para agregar anotaciones a tu proyecto, debes descargar el repositorio de compatibilidad y agregar la dependencia support-annotations a tu archivo build.gradle.

  1. Para abrir SDK Manager, haz clic en SDK Manager en la barra de herramientas o selecciona Tools > Android > SDK Manager.
  2. Haz clic en la pestaña SDK Tools.
  3. Expande Support Repository y selecciona la casilla de verificación Android Support Repository.
  4. Haz clic en OK.
  5. Continúa con los pasos siguientes del asistente para instalar los paquetes.
  6. Agrega la dependencia support-annotations a tu proyecto; para ello, dispón la siguiente línea en el bloque dependencies de tu archivo build.gradle:
     dependencies { compile 'com.android.support:support-annotations:24.2.0' } 
    La versión de la biblioteca que descargues puede ser superior; por lo tanto, asegúrate de que el valor especificado aquí coincida con la versión del paso 3.
  7. 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, se incluyen como parte del artefacto de archivo de Android (AAR) en formato XML en el archivo annotations.zip. Agregar la dependencia support-annotations no introduce una dependencia para usuarios intermedios de tu biblioteca.

Si deseas usar anotaciones en un módulo de Gradle que no use el complemento de Android para Gradle (com.android.application o com.android.library), pero que emplee el complemento de Java para Gradle en su lugar, debes incluir el repositorio de SDK de forma explícita debido a que las bibliotecas de compatibilidad de Android no están disponibles en el repositorio Java JCenter:

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

Nota: si usas la biblioteca appcompat, no es necesario que agregues la dependencia support-annotations. Debido a que la biblioteca appcompat ya depende de la biblioteca de anotaciones, puedes acceder a estas últimas.

Para obtener una lista completa de las anotaciones incluidas en el repositorio de compatibilidad, examina la referencia sobre la biblioteca de anotaciones de asistencia o usa la función de autocompletado para mostrar las opciones disponibles para la instrucción import android.support.annotation..

Ejecutar inspecciones de código

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

También puedes implementar anotaciones ejecutando la tarea lint mediante la línea de comandos. Si bien esto puede resultar útil para marcar problemas con un servidor de integración continuo, ten en cuenta que la tarea lint no permite implementar anotaciones de nulidad (solo Android Studio lo hace). Para obtener más información sobre cómo habilitar y ejecutar inspecciones de Lint, consulta Cómo mejorar tu código con Lint.

Ten en cuenta que, si bien los conflictos de anotaciones generan advertencias, estas no impiden que tu app realice compilaciones.

Anotaciones de nulidad

Agrega las anotaciones @Nullable y @NonNull para verificar la nulidad de una variable, un parámetro o un valor de retorno determinado. La anotación @Nullable permite indicar una variable, un parámetro o un valor de retorno que puede ser nulo, mientras que la anotación @NonNull permite indicar una variable, un parámetro o un valor de retorno 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 dicho parámetro, durante la compilación del código se genera una advertencia mediante la que se indica un conflicto de no nulidad. Por otro lado, 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 de retorno de un método si se debe verificar explícitamente la nulidad de cada uso de dicho método.

En el siguiente ejemplo, se adjunta la anotación @NonNull a los parámetros context y attrs para verificar que los valores de parámetro que se pasan no sean nulos. Además, se verifica que con el método onCreateView() no se muestre un resultado nulo:

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) {
      ...
      }
...

Análisis de nulabilidad

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

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 verificar las anotaciones insertadas.

Nota: cuando se agregan anotaciones de nulidad, el autocompletado puede sugerir las anotaciones @Nullable y @NotNull de IntelliJ en lugar de las anotaciones de valor nulo de Android, y se puede importar la biblioteca correspondiente de manera automática. Sin embargo, el revisor Lint de Android Studio solo detecta las anotaciones de valor nulo de Android. Cuando verifiques tus anotaciones, confirma que en tu 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 los recursos drawable y string, se pasan como valores enteros. Un código para el cual se prevea que un parámetro haga referencia a un tipo de recurso específico, como Drawables, puede recibir el tipo de referencia int esperado, pero en realidad puede hacer referencia a un tipo de recurso diferente; por ejemplo, R.string.

Por ejemplo, agrega anotaciones @StringRes para verificar que un parámetro de recurso contenga una referencia R.string, como se muestra aquí:

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 demás tipos de recursos (por ejemplo, @DrawableRes, @DimenRes, @ColorRes y @InterpolatorRes) usando 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 disponer más de una de estas anotaciones en un parámetro determinado. Usa @AnyRes para indicar que el parámetro anotado puede ser cualquier tipo de recurso R.

Si bien puedes usar @ColorRes para especificar que un parámetro debe ser un recurso de color, un valor entero de color (en el formato RRGGBB o AARRGGBB) no se reconoce 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 enviarán una advertencia si un código incorrecto pasa un ID de recurso de color, como android.R.color.black, en lugar de un valor entero de color a los métodos anotados.

Anotaciones de subprocesos

Mediante 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:

Nota: 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 con varias vistas en distintos subprocesos. Por lo tanto, debes anotar con @UiThread los métodos vinculados a la jerarquía de vistas de una app 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 la anotación de subproceso es validar las anulaciones de métodos en la clase AsyncTask debido a que esta clase lleva a cabo operaciones en segundo plano y publica los resultados solo en el subproceso de la IU.

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 los usuarios probablemente obtengan el rango incorrecto.

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

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

La anotación @FloatRange permite verificar que un valor de parámetro doble o flotante se encuentre dentro de un rango especificado de valores de punto flotante. En el siguiente ejemplo, se garantiza que el parámetro alpha contenga un valor flotante de 0,0 a 1,0:

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

La anotación @Size permite verificar el tamaño de un conjunto o una matriz y también la extensión de una string. La anotación @Size puede usarse para verificar los siguientes atributos:

Por ejemplo, @Size(min=1) permite verificar si un conjunto no está vacío y @Size(3) permite validar que una matriz contenga exactamente tres valores. En el siguiente ejemplo, se garantiza que la matriz location contenga al menos un elemento:

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

Anotaciones de permisos

Usa la anotación @RequiresPermission para validar los permisos del emisor 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 anota el método setWallpaper() para garantizar que el emisor de este tenga el permiso permission.SET_WALLPAPERS:

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

En este ejemplo, se requiere que el emisor del método copyFile() tenga permisos de lectura y escritura con respecto al almacenamiento externo:

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

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

@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 para los cuales necesites permisos de lectura y escritura por separado, encapsula cada requisito de permiso en una anotación @RequiresPermission.Read o @RequiresPermission.Write :

@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 proporcionado al parámetro de un método, usa @RequiresPermission en este sin incluir una lista de los permisos específicos. Por ejemplo, el método startActivity(Intent) usa un permiso indirecto sobre la intent que se pasa al método:

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 una intent sin los permisos correspondientes al método, como se muestra en la figura 1.

Figura 1: Advertencia generada a partir de una anotación de permiso indirecto sobre 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 la intent correspondiente en la clase Intent:

@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@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 crean anotaciones sobre el parámetro de un método. Sin embargo, en el caso de los permisos indirectos, @RequiresPermission no debe usarse en conjunto con las anotaciones de permisos de lectura o escritura.

Anotaciones de valor de retorno

Usa la anotación @CheckResult para validar el uso efectivo del resultado o el valor de retorno 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 piensan erróneamente que <String>.trim() permite quitar los espacios en blanco de la string original. La anotación del método con @CheckResult permite marcar los usos de <String>.trim() con los cuales el emisor no realiza ninguna acción con el valor de retorno del método.

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

@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():

@CallSuper
protected void onCreate(Bundle savedInstanceState) {
}

Anotaciones typedef

Usa las anotaciones @IntDef y @StringDef para crear anotaciones enumeradas de conjuntos de strings y valores enteros para validar otros tipos de referencias de código. Este tipo de anotaciones permite garantizar que un parámetro, valor de retorno o campo en particular haga referencia a un conjunto de constantes específico. Además, permiten habilitar la compleción del código para ofrecer las constantes permitidas de manera automática.

En las anotaciones typedef, 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:

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);

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

También puedes combinar @IntDef y @IntRange para indicar que un valor entero puede ser un conjunto determinado de constantes o un valor dentro de un rango.

Habilitar la combinación de constantes con indicadores

Si los usuarios pueden combinar las constantes permitidas con un indicador (por ejemplo, |, &, ^, etc.), puedes definir una anotación con un atributo flag para verificar si un parámetro o un valor de retorno 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:

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 {}

...

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

Anotaciones de accesibilidad de código

Usa las anotaciones @VisibleForTesting y @Keep para señalar la accesibilidad de un método, una clase o un campo.

La anotación @VisibleForTesting indica que un bloque de código es más visible de lo necesario para que se puedan hacer pruebas en este.

La anotación @Keep permite garantizar que no se quite un elemento anotado cuando se minifique el código durante el tiempo de compilación. Por lo general, se agrega a métodos y clases a los que se accede mediante reflexión para evitar que el compilador lo considere como código no usado.

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a one-minute survey?
Help us improve Android tools and documentation.