Retrocompatibilidad para el entorno de ejecución de SDK

En este documento, se propone una nueva biblioteca de Jetpack para ayudar a los desarrolladores con la migración al entorno de ejecución de SDK. Explica cómo se admitirá el entorno de ejecución de SDK para versiones anteriores de la plataforma de Android (desde la compilación hasta la ejecución) y qué diferencias o limitaciones del entorno de ejecución pueden esperar los desarrolladores. Esta biblioteca permite a los desarrolladores crear una versión única de su app o SDK que incluya la capacidad de ejecutarse en dispositivos con o sin compatibilidad con el entorno de ejecución de SDK.

La retrocompatibilidad se logra a través de los siguientes componentes:

  • El complemento de Android para Gradle (AGP) + Bundletool compila una variante de app para dispositivos que no son compatibles con el entorno de ejecución de SDK empaquetando el entorno de ejecución de SDK en el APK.

  • La biblioteca cliente del entorno de ejecución de SDK (androidx.privacysandbox.sdkruntime:sdkruntime-client) carga el SDK en paquete desde los recursos de la app y emula el entorno de ejecución de SDK en dispositivos que no son compatibles con el entorno de ejecución de SDK.

  • La biblioteca del proveedor del entorno de ejecución de SDK (androidx.privacysandbox.sdkruntime:sdkruntime-provider) proporciona una API para que los SDKs permitan la carga desde la biblioteca cliente del entorno de ejecución de SDK.

Publicación del SDK con Bundletool

En los dispositivos compatibles con el entorno de ejecución de SDK, los SDKs se entregarán y se instalarán como paquetes separados.

Para admitir versiones de plataforma que no son compatibles con el entorno de ejecución de SDK, Bundletool compilará una o más variantes del conjunto de APK de la app que incluyan todos los SDK de los que depende la app. Cada SDK se empaqueta como una división del APK independiente. Además, se realizan las siguientes transformaciones:

  1. Copia los archivos del código de bytes del SDK (DEX) en el SDK divididos como elementos.
  2. Copia los recursos de Java del SDK en el SDK divididos como elementos.
  3. Reasigna los recursos del SDK y combínalos con los recursos de la app.
  4. Genera archivos de configuración para la biblioteca cliente del entorno de ejecución de SDK.

Carga los SDKs con la biblioteca cliente del entorno de ejecución de SDK

La biblioteca cliente del entorno de ejecución de SDK proporciona APIs que son similares a las APIs de la plataforma, pero admiten los SDKs en el entorno del entorno de ejecución de SDK y los SDKs agrupados con la app de la variante.

Para usar la biblioteca cliente del entorno de ejecución de SDK, agrega la dependencia androidx.privacysandbox.sdkruntime:sdkruntime-client y usa SdkSandboxManagerCompat en lugar de SdkSandboxManager.

Cuando una app intenta cargar un SDK, la biblioteca primero comprueba si el SDK se empaqueta con la app durante la compilación. Si se empaqueta, la biblioteca extrae el SDK de la división del SDK y lo carga en el proceso de la app. Si el SDK no se empaqueta con la app, la biblioteca delega la API de la plataforma para cargar el SDK.

Extrae un SDK de los recursos

Cuando una app intenta cargar un SDK empaquetado, la biblioteca cliente del entorno de ejecución de SDK verifica si los archivos DEX del SDK ya se extrajeron al almacenamiento del dispositivo (code_cache) y, si no, los extrae de los recursos.

En general, la biblioteca extraerá archivos solo una vez después de instalar o actualizar una app.

Si el espacio de almacenamiento disponible es inferior al umbral permitido (actualmente, 100 MB) y no se extraen archivos DEX, la biblioteca intentará cargar el SDK directamente desde los elementos en dispositivos compatibles (nivel de API 27 o superior), lo que genera un uso de memoria más grande.

Cargador de clases para clases de SDK

Para evitar conflictos entre los SDKs y las clases de la app, todas las clases de SDK se cargan con un cargador de clases independiente y completamente independiente del cargador de clases principal de la app.

En el diseño actual del entorno de ejecución de SDK, todas las comunicaciones entre una app y los SDKs se realizan mediante llamadas IPC de Binder. Los mismos objetos de Binder del SDK se usan para los SDKs agrupados, y la serialización de transacciones de Binder permite a los desarrolladores de apps transmitir objetos de Binder de SDK a interfaces de Binder de SDK en el lado de la app.

Para otras interacciones internas (como inicializar un SDK, proporcionar una API de controlador a un SDK, etc.), la biblioteca usa Proxies dinámicos y la reflexión para funcionar en diferentes cargadores de clases.

Entorno del SDK

La biblioteca del proveedor de SDKRuntime proporciona APIs a los desarrolladores de SDK. Estas APIs son similares a las APIs de la plataforma, pero permiten que el entorno de ejecución de SDK y la biblioteca cliente de SDKRuntime carguen los SDKs.

Para poder usar el SDK de la biblioteca, debes agregar la dependencia androidx.privacysandbox.sdkruntime:sdkruntime-provider y extender SandboxedSdkProviderCompat en lugar de SandboxedSdkProvider.

También debes usar SandboxedSdkProviderAdapter como el proveedor del SDK para permitir que el proveedor de compatibilidad se cargue en el entorno de ejecución del SDK.

SdkSandboxControllerCompat delega a la API de la plataforma cuando el SDK se carga en el entorno de ejecución de SDK o delega a la biblioteca cliente de SDKRuntime cuando el SDK se carga como un SDK empaquetado.

En el caso de los SDKs agrupados, la biblioteca modifica el entorno del SDK de formas que emulan un comportamiento similar al del entorno de ejecución de SDK.

En las siguientes secciones, se describen los comportamientos esperados cuando la biblioteca cliente de SDKRuntime carga el SDK.

Recursos del SDK

Los recursos del SDK (res/) se admiten cuando el SDK se carga en el proceso de la app. Bundletool combina todos los recursos de los SDK con los recursos de la app.

Para evitar conflictos, se reasignan los recursos del SDK cambiando el prefijo packageId en todos los IDs de recursos.

Cuando la biblioteca cliente de SDKRuntime carga el SDK, se actualiza packageId en el entorno de ejecución para permitir la asignación de recursos reasignados mediante la clase R.

Recursos de Java

Los recursos Java son compatibles cuando el SDK se carga en el proceso de la app. Bundletool copia todos los recursos del SDK de Java en un directorio especial, en los recursos de app. La biblioteca cliente SDKRuntime usa un cargador de clases intermedio para redireccionar todas las llamadas relacionadas con los recursos de Java al nuevo directorio raíz.

Elementos del SDK

Los elementos del SDK se combinan con los recursos de aplicación sin reasignarlos.

Almacenamiento del SDK

Para admitir el almacenamiento de SDK, la biblioteca cliente del entorno de ejecución de SDK crea un directorio raíz dedicado para cada SDK agrupado en el almacenamiento de la app y proporciona un contexto especial que usa este directorio como raíz de almacenamiento.

Este contexto se puede recuperar desde SandboxedSdkProviderCompat#getContext.

Métodos compatibles relacionados con el almacenamiento:

  • getDataDir
  • getCacheDir
  • getCodeCacheDir
  • getNoBackupFilesDir
  • getDir
  • getFilesDir
  • openFileInput
  • openFileOutput
  • deleteFile
  • getFileStreamPath
  • fileList
  • getDatabasePath
  • openOrCreateDatabase
  • moveDatabaseFrom: Solo entre contextos de SDK
  • deleteDatabase
  • databaseList
  • getSharedPreferences
  • moveSharedPreferencesFrom: Solo entre contextos de SDK
  • deleteSharedPreferences

Se puede crear un contexto de almacenamiento protegido por el dispositivo llamando a createDeviceProtectedStorageContext() en ese contexto.

SdkSandboxControllerCompat

La biblioteca cliente de SDKRuntime proporciona la implementación de SdkSandboxControllerCompat para los SDK agrupados que se cargan en el proceso de la app.

Si la biblioteca cliente no admite las APIs (por ejemplo, con un SDK compilado con una versión de la biblioteca más reciente que la versión de la app), se usará el resguardo más adecuado (no-ops o excepción).

Control de versiones

Cuando la biblioteca cliente de SDKRuntime carga un SDK empaquetado, realiza un protocolo de enlace con la biblioteca del proveedor de SDKRuntime dentro del SDK. Durante el protocolo de enlace, las bibliotecas intercambian sus versiones y ajustan el comportamiento para sustituir las APIs no disponibles por el resguardo más adecuado (no-ops o excepción).

Se recomienda usar la versión más reciente de la biblioteca tanto para los desarrolladores de apps como para los de SDK. De lo contrario, es posible que la funcionalidad que requiera compatibilidad en ambas partes no esté disponible.

Cualquier versión de la biblioteca cliente de SDKRuntime puede cargar un SDK con cualquier versión de la biblioteca del proveedor de SDKRuntime y viceversa.

En el futuro, se cambiará a la versión mínima de la biblioteca cliente necesaria para cargar el SDK con una versión específica de la biblioteca del proveedor.

Esto minimizará la fragmentación y ayudará a garantizar que la mayoría de las APIs sean compatibles si el SDK en paquete se cargó correctamente.