Cómo implementar la biblioteca del SDK de Java

La plataforma de Android contiene una gran cantidad de bibliotecas de Java compartidas que pueden incluirse opcionalmente en la ruta de clase de las apps con la etiqueta <uses-library> en el manifiesto de la app. Las apps se vinculan con estas bibliotecas, por lo que debes tratarlas como al resto de las APIs de Android en términos de compatibilidad, revisión de la API y compatibilidad con herramientas. Sin embargo, ten en cuenta que la mayoría de las bibliotecas no tienen estas funciones.

El tipo de módulo java_sdk_library ayuda con la administración de bibliotecas de esta clase. Los fabricantes de dispositivos pueden usar este mecanismo en sus propias bibliotecas de Java compartidas para mantener la retrocompatibilidad con las APIs. Si los fabricantes de dispositivos usan sus propias bibliotecas de Java compartidas a través de la etiqueta <uses-library> en lugar de la ruta de inicio de la clase, java_sdk_library puede verificar que esas bibliotecas de Java sean estables para la API.

java_sdk_library implementa APIs del SDK opcionales para uso de las apps. Las bibliotecas implementadas con java_sdk_library en tu archivo de compilación (Android.bp) llevan a cabo las siguientes operaciones:

  • Se generan bibliotecas de stubs para incluir stubs, stubs.system y stubs.test. Esto se hace a través del reconocimiento de las anotaciones @hide, @SystemApi y @TestApi.
  • java_sdk_library administra los archivos de especificación de API (como current.txt) en un subdirectorio de la API. Estos archivos se comparan con el código más reciente para garantizar que sean las versiones más actualizadas. Si no lo son, recibirás un mensaje de error que explica cómo actualizarlos. Revisa de forma manual todos los cambios de la actualización para asegurarte de que cumplan con tus expectativas.

    Para actualizar todas las APIs, usa m update-api. Si deseas verificar que una API esté actualizada, usa m checkapi.
  • Los archivos de especificación de la API se comparan con las versiones más recientes de Android que se hayan publicado para garantizar que la API sea retrocompatible con las versiones anteriores. Los módulos java_sdk_library proporcionados como parte de AOSP ubican sus versiones anteriores en prebuilts/sdk/<latest number>.
  • Con respecto a las verificaciones de archivos de especificación de la API, puedes realizar una de las siguientes tres acciones:
    • Permitir que se hagan las verificaciones para continuar (sin acciones)
    • Agregar lo siguiente a java_sdk_library para inhabilitar las verificaciones:
      unsafe_ignore_missing_latest_api: true,
    • Crear archivos de texto vacíos llamados module_name.txt en el directorio version/scope/api y proporcionar APIs vacías para nuevos módulos java_sdk_library
  • Si se instaló la biblioteca de implementación para el tiempo de ejecución, se generará e instalará un archivo XML.

Cómo funciona java_sdk_library

Un elemento java_sdk_library llamado X crea lo siguiente:

  1. Dos copias de la biblioteca de implementación: una que se llama X y otra que se llama X.impl. La biblioteca X está instalada en el dispositivo. La biblioteca X.impl, en cambio, solo se instala si otros módulos necesitan acceso explícito a la biblioteca de implementación, por ejemplo, para usarla en pruebas. Ten en cuenta que rara vez se necesita un acceso explícito.
  2. Se pueden habilitar o inhabilitar los alcances para personalizar el acceso (al igual que los modificadores de acceso de palabras clave de Java, un alcance público proporciona un amplio rango de acceso; un alcance de prueba contiene las APIs que solo se usan en pruebas). Para cada alcance habilitado, la biblioteca crea los siguientes elementos:
    • Un módulo de origen de stub (de tipo de módulo droidstubs): Consume la fuente de implementación y da como resultado un conjunto de fuentes de stub junto con el archivo de especificación de la API.
    • Una biblioteca de stubs (de tipo de módulo java_library): Es la versión compilada de los stubs. Las bibliotecas que se usan para compilar esto no son las mismas que se proporcionan a java_sdk_library, lo que garantiza que los detalles de implementación no se filtren en los stubs de la API.
    • Si necesitas bibliotecas adicionales para compilar los stubs, usa las propiedades stub_only_libs y stub_only_static_libs para proporcionarlas.

Si una java_sdk_library se llama "X" y se está compilando como "X", siempre haz referencia a ella de esa manera y no la modifiques. La compilación seleccionará una biblioteca adecuada. Para asegurarte de tener la biblioteca más adecuada, inspecciona tus stubs y revisa si la compilación introdujo errores. Haz las correcciones necesarias con esta guía:

  • Verifica que la biblioteca sea adecuada. Para ello, observa la línea de comandos y consulta qué stubs se enumeran allí para determinar el alcance:
    • El alcance es demasiado amplio: La biblioteca dependiente necesita un cierto alcance de las APIs. Sin embargo, verás APIs incluidas en la biblioteca que quedan fuera de ese alcance, como las APIs del sistema incluidas con las APIs públicas.
    • El alcance es demasiado limitado: La biblioteca dependiente no tiene acceso a todas las bibliotecas requeridas. Por ejemplo, la biblioteca dependiente necesita usar la API del sistema, pero recibe la API pública en su lugar. Por lo general, esto genera un error de compilación porque faltan las APIs necesarias.
  • Para corregir la biblioteca, realiza solo una de las siguientes acciones:
    • Cambia sdk_version para seleccionar la versión que necesitas. O BIEN
    • Especifica de forma explícita la biblioteca adecuada, como <X>.stubs o <X>.stubs.system.

Uso de la java_sdk_library X

La biblioteca de implementación X se usa cuando se hace referencia a ella desde apex.java_libs. Sin embargo, debido a una limitación de Soong, si se hace referencia a la biblioteca X desde otro módulo java_sdk_library dentro de la misma biblioteca de APEX, se debe usar explícitamente X.impl en lugar de la biblioteca X.

Cuando se hace referencia a java_sdk_library desde otro lugar, se usa una biblioteca de stubs. La biblioteca de stubs se selecciona según el parámetro de configuración de la propiedad sdk_version del módulo dependiente. Por ejemplo, un módulo que especifica sdk_version: "current" usa stubs públicos, mientras que un módulo que especifica sdk_version: "system_current" usa stubs del sistema. Si no se encuentra una concordancia exacta, se usa la biblioteca de stubs más cercana. Un java_sdk_library que solo proporciona una API pública proporcionará los stubs públicos para todos.

Flujo de compilación con la biblioteca del SDK de Java
Figura 1: Flujo de compilación con la biblioteca del SDK de Java

Ejemplos y fuentes

Las propiedades srcs y api_packages deben estar presentes en el java_sdk_library.

java_sdk_library {
        name: "com.android.future.usb.accessory",
        srcs: ["src/**/*.java"],
        api_packages: ["com.android.future.usb"],
    }

AOSP recomienda (pero no requiere) que las nuevas instancias de java_sdk_library habiliten de forma explícita los alcances de la API que se quieran usar. También puedes migrar (de manera opcional) las instancias existentes de java_sdk_library para habilitar explícitamente los alcances de la API que usarán:

java_sdk_library {
         name: "lib",
         public: {
           enabled: true,
         },
         system: {
           enabled: true,
         },
         …
    }

Para configurar la biblioteca impl que se usará durante la ejecución, utiliza todas las propiedades normales de java_library, como hostdex, compile_dex y errorprone.

java_sdk_library {
        name: "android.test.base",

        srcs: ["src/**/*.java"],

        errorprone: {
          javacflags: ["-Xep:DepAnn:ERROR"],
        },

        hostdex: true,

        api_packages: [
            "android.test",
            "android.test.suitebuilder.annotation",
            "com.android.internal.util",
            "junit.framework",
        ],

        compile_dex: true,
    }

Para configurar bibliotecas de stubs, usa las siguientes propiedades:

  • merge_annotations_dirs y merge_inclusion_annotations_dirs.
  • api_srcs: Es la lista de archivos de origen opcionales que forman parte de la API, pero no de la biblioteca del entorno de ejecución.
  • stubs_only_libs: Es la lista de bibliotecas de Java que se encuentran en la ruta de clase cuando se compilan stubs.
  • hidden_api_packages: Es la lista de nombres de paquetes que deben estar ocultos de la API.
  • droiddoc_options: Es un argumento adicional de metalava.
  • droiddoc_option_files: Enumera los archivos a los que se puede hacer referencia desde droiddoc_options con $(location <label>), en el que <file> es una entrada en la lista.
  • annotations_enabled.

java_sdk_library es una java_library, pero no es un módulo droidstubs y, por lo tanto, no es compatible con todas las propiedades de droidstubs. El siguiente ejemplo se tomó del archivo de compilación de biblioteca android.test.mock.

java_sdk_library {
        name: "android.test.mock",

        srcs: [":android-test-mock-sources"],
        api_srcs: [
            // Note: The following aren’t APIs of this library. Only APIs under the
            // android.test.mock package are taken. These do provide private APIs
            // to which android.test.mock APIs reference. These classes are present
            // in source code form to access necessary comments that disappear when
            // the classes are compiled into a Jar library.
            ":framework-core-sources-for-test-mock",
            ":framework_native_aidl",
        ],

        libs: [
            "framework",
            "framework-annotations-lib",
            "app-compat-annotations",
            "Unsupportedappusage",
        ],

        api_packages: [
            "android.test.mock",
        ],
        permitted_packages: [
            "android.test.mock",
        ],
        compile_dex: true,
        default_to_stubs: true,
    }

Cómo mantener la retrocompatibilidad

El sistema de compilación verifica si las APIs conservaron la retrocompatibilidad comparando los archivos de API más recientes con los que se generaron al momento de la compilación. java_sdk_library verifica la compatibilidad con la información proporcionada por prebuilt_apis. Todas las bibliotecas compiladas con java_sdk_library deben tener archivos de API en la versión más reciente de api_dirs en prebuilt_apis. Cuando lanzas la versión, la API enumera los archivos, y se pueden obtener las bibliotecas de stubs con compilación dist con PRODUCT=sdk_phone_armv7-sdk.

La propiedad api_dirs es una lista de directorios de versiones de API en prebuilt_apis. Los directorios de la versión de la API deben estar ubicados en el nivel del directorio Android.bp.

prebuilt_apis {
       name: "foo",
       api_dirs: [
           "1",
           "2",
             ....
           "30",
           "current",
       ],
    }

Configura los directorios con la estructura version/scope/api/ en el directorio de compilaciones previas. version corresponde al nivel de API y scope define si el directorio es público, del sistema o de prueba.

  • version/scope contiene bibliotecas Java.
  • version/scope/api contiene archivos .txt de API. Crea archivos de texto vacíos llamados module_name.txt y module_name-removed.txt aquí.
     ├── 30
          │   ├── public
          │   │   ├── api
          │   │   │   ├── android.test.mock-removed.txt
          │   │   │   └── android.test.mock.txt
          │   │   └── android.test.mock.jar
          │   ├── system
          │   │   ├── api
          │   │   │   ├── android.test.mock-removed.txt
          │   │   │   └── android.test.mock.txt
          │   │   └── android.test.mock.jar
          │   └── test
          │       ├── api
          │       │   ├── android.test.mock-removed.txt
          │       │   └── android.test.mock.txt
          │       └── android.test.mock.jar
          └── Android.bp