Dispositivos de varios núcleos y Xamarin.Android

Android puede ejecutarse en varias arquitecturas de equipo diferentes. En este documento se describen las distintas arquitecturas de CPU que se pueden emplear con una aplicación Xamarin.Android. También se explica cómo las aplicaciones Android se empaquetan para admitir distintas arquitecturas de CPU. Se introducirá la Interfaz binaria de aplicación (ABI) y se proporcionarán instrucciones respecto a cuáles ABI usar en una aplicación Xamarin.Android.

Información general

Android permite la creación de "fat binaries" (también conocidos como binarios multiarquitectura), un único archivo .apk que contiene código máquina que admitirá varias arquitecturas diferentes de CPU. Para ello, cada trozo de código máquina se asocia con una Interfaz binaria de aplicación. La ABI se usa para controlar qué código máquina se ejecutará en un dispositivo de hardware determinado. Por ejemplo, para que una aplicación Android se ejecute en un dispositivo x86, es necesario incluir compatibilidad con la ABI x86 al compilar la aplicación.

En concreto, cada aplicación Android admitirá al menos una interfaz binaria de aplicación insertada (EABI). Las EABI son convenciones específicas de programas de software insertados. Por lo general, una EABI describe cosas como:

  • El conjunto de instrucciones de CPU.

  • El orden secuencial (endianness) en que la memoria se almacena y carga en tiempo de ejecución.

  • El formato binario de archivos de objetos y bibliotecas de programa, así como el tipo de contenido que se permite o admite en estos archivos y bibliotecas.

  • Las diversas convenciones usadas para pasar datos entre el código de aplicación y el sistema (por ejemplo, cómo se usan los registros o la pila al llamar a funciones, las restricciones de alineación, etc.)

  • Restricciones de tamaño y alineación para tipos de enumeración, estructuras, campos y matrices.

  • La lista de símbolos de función disponibles para el código máquina en tiempo de ejecución, en general desde un conjunto de bibliotecas muy específico seleccionado.

armeabi y la seguridad para subprocesos

A continuación se examina en detalle la Interfaz binaria de aplicación, pero es importante recordar que el entorno de tiempo de ejecución de armeabi que se usa en Xamarin.Android no es seguro para subprocesos. Si una aplicación que tiene compatibilidad con armeabi se implementa en un dispositivo armeabi-v7a, se producirán muchas excepciones raras e inexplicables.

Debido a un error en Android 4.0.0, 4.0.1, 4.0.2 y 4.0.3, las bibliotecas nativas se seleccionarán del directorio armeabi aun en el caso de que haya un directorio armeabi-v7a y el dispositivo sea un dispositivo armeabi-v7a.

Nota:

Xamarin.Android garantiza que .so se agrega al APK en el orden correcto. Este error no debería ser un problema para los usuarios de Xamarin.Android.

Descripciones de ABI

Cada ABI compatible con Android se identifica mediante un nombre único.

armeabi

Este es el nombre de una EABI para CPU basadas en ARM que admiten al menos el conjunto de instrucciones ARMv5TE. Android sigue la ABI GNU/Linux de ARM little endian. Esta ABI no es compatible con cálculos de punto flotante asistidos por hardware. Todas las operaciones de FP se realizan mediante funciones del asistente de software que proceden de la biblioteca estática libgcc.a del compilador. Los dispositivos SMP no admiten armeabi.

Importante

El código armeabi de Xamarin.Android no es seguro para subprocesos y no se debe usar en dispositivos armeabi-v7a de varios núcleos (se describen a continuación). El uso de código armeabi en un dispositivo armeabi-v7a de un único núcleo es seguro.

armeabi-v7a

Se trata de otro conjunto de instrucciones de CPU basado en ARM que amplía la EABI armeabi descrita anteriormente. La EABI armeabi-v7a presenta compatibilidad con operaciones de punto flotante de hardware y varios dispositivos de CPU (SMP). Las aplicaciones que usan la EABI armeabi-v7a pueden esperar considerables mejoras en el rendimiento por encima de las aplicaciones que usan armeabi.

Nota:

El código máquina armeabi-v7a no se ejecuta en dispositivos ARMv5.

arm64-v8a

Se trata de un conjunto de instrucciones de 64 bits que se basa en la arquitectura de CPU de ARMv8. Esta arquitectura se usa en Nexus 9. Xamarin.Android 5.1 ha introducido soporte técnico para esta arquitectura (para más información, consulte 64-bit runtime support [Soporte técnico del entorno de ejecución de 64 bits]).

x86

Este es el nombre de una ABI para CPU que admiten el conjunto de instrucciones conocido comúnmente como x86 o IA-32. Esta ABI corresponde a las instrucciones del conjunto de instrucciones Pentium Pro, que incluye los conjuntos de instrucciones MMX, SSE, SSE2 y SSE3. No incluye ninguna otra extensión opcional de conjunto de instrucciones IA-32 como:

  • La instrucción MOVBE.
  • La extensión SSE3 complementaria (SSSE3).
  • Cualquier variante de SSE4.

Nota:

Aunque se ejecuta en x86, Google TV no es compatible con el NDK de Android.

x86_64

Este es el nombre de una ABI para CPU que admiten el conjunto de instrucciones x86 de 64 bits (también conocido como x64 o AMD64). Xamarin.Android 5.1 ha introducido soporte técnico para esta arquitectura (para más información, consulte 64-bit runtime support [Soporte técnico del entorno de ejecución de 64 bits]).

Formato de archivo de APK

El paquete de aplicaciones Android es el formato de archivo que contiene todo el código, los activos, los recursos y los certificados necesarios para una aplicación Android. Es un archivo .zip, pero usa la extensión de nombre de archivo .apk. Cuando se expande, se puede ver el contenido de un archivo .apk creado en Xamarin.Android en la captura de pantalla siguiente:

Contents of the .apk

Una descripción rápida del contenido del archivo .apk:

  • AndroidManifest.xml: este es el archivo AndroidManifest.xml, en formato XML binario.

  • classes.dex: contiene el código de la aplicación, compilado en el formato de archivo dex que usa la máquina virtual en tiempo de ejecución de Android.

  • resources.arsc: este archivo contiene todos los recursos precompilados de la aplicación.

  • lib: este directorio contiene el código compilado de cada ABI. Contendrá una subcarpeta para cada ABI que se ha descrito en la sección anterior. En la captura de pantalla anterior, el archivo .apk en cuestión tiene bibliotecas nativas para armeabi-v7a y para x86.

  • META-INF: este directorio (si existe) se usa para almacenar información de firma, paquetes y datos de configuración de extensión.

  • res: este directorio contiene los recursos que no se compilaron en resources.arsc.

Nota:

El archivo libmonodroid.so es la biblioteca nativa que necesitan todas las aplicaciones Xamarin.Android.

Compatibilidad con ABI en dispositivos Android

Cada dispositivo Android admite la ejecución de código nativo en hasta dos ABI:

  • ABI "principal": corresponde al código máquina usado en la imagen del sistema.

  • ABI "secundaria": ABI opcional que también es compatible con la imagen del sistema.

Por ejemplo, normalmente un dispositivo ARMv5TE solo tendrá una ABI principal de armeabi, mientras que un dispositivo ARMv7 especificaría una ABI principal de armeabi-v7a y una ABI secundaria de armeabi. Normalmente, un dispositivo x86 solo debería especificar una ABI principal de x86.

Instalación de bibliotecas nativas de Android

En el momento de la instalación del paquete, las bibliotecas nativas que contiene el archivo .apk se extraen en el directorio de biblioteca nativa de la aplicación, normalmente /data/data/<package-name>/lib y, por tanto, se conocen como $APP/lib.

El comportamiento de la instalación de bibliotecas nativas de Android varía considerablemente según la versión de Android.

Instalación de bibliotecas nativas: con anterioridad a Android 4.0

Las versiones de Android anteriores a 4.0 Ice Cream Sandwich solo extraían las bibliotecas nativas de una única ABI dentro del archivo .apk. Las aplicaciones Android de esta hornada primero intentan extraer todas las bibliotecas nativas de la ABI principal, y si no están ahí, de la ABI secundaria. No se realiza ninguna combinación.

Por ejemplo, considere una situación donde una aplicación está instalada en un dispositivo armeabi-v7a. El archivo .apk, que admite armeabi y armeabi-v7a, contiene los siguientes directorios y archivos lib de ABI:

lib/armeabi/libone.so
lib/armeabi/libtwo.so
lib/armeabi-v7a/libtwo.so

Después de la instalación, el directorio de bibliotecas nativas contendrá:

$APP/lib/libtwo.so # from the armeabi-v7a directory in the apk

En otras palabras, no se instala ningún archivo libone.so. Como consecuencia, se producen problemas, dado que libone.so no existe para que la aplicación se cargue en tiempo de ejecución. Este comportamiento, aunque inesperado, se ha registrado como un error y se vuelve a clasificar como que funciona según lo esperado.

Por lo tanto, cuando el destino son versiones de Android anteriores a la 4.0, es necesario proporcionar todas las bibliotecas nativas para cada ABI que admita la aplicación, es decir, el archivo .apk debe contener:

lib/armeabi/libone.so
lib/armeabi/libtwo.so
lib/armeabi-v7a/libone.so
lib/armeabi-v7a/libtwo.so

Instalación de bibliotecas nativas: Android 4.0 – Android 4.0.3

Android 4.0 Ice Cream Sandwich cambia la lógica de extracción. Enumera todas las bibliotecas nativas, comprueba si el nombre base del archivo ya se ha extraído y, si se cumplen las dos siguientes condiciones, se extrae la biblioteca:

  • Aún no se ha extraído.

  • La ABI de la biblioteca nativa corresponde a la ABI principal o secundaria del destino.

Al cumplirse estas dos condiciones, es posible un comportamiento de "combinación"; es decir, si tenemos un archivo .apk con el siguiente contenido:

lib/armeabi/libone.so
lib/armeabi/libtwo.so
lib/armeabi-v7a/libtwo.so

Tras la instalación, el directorio de biblioteca nativa contendrá:

$APP/lib/libone.so
$APP/lib/libtwo.so

Por desgracia, este comportamiento depende del orden, como se describe en el documento siguiente Issue 24321: Galaxy Nexus 4.0.2 uses armeabi native code when both armeabi and armeabi-v7a is included in apk (Problema 24321: Galaxy Nexus 4.0.2 usa código nativo de armeabi cuando armeabi y armeabi-v7a se incluyen en apk).

Las bibliotecas nativas se procesan "en orden" (como se enumeran al descomprimir, por ejemplo) y se extrae la primera coincidencia. Dado que el archivo .apk contiene las versiones armeabi y armeabi-v7a de libtwo.so, y armeabi se muestra en primer lugar, la versión que se extrae es armeabi y no la versión armeabi-v7a:

$APP/lib/libone.so # armeabi
$APP/lib/libtwo.so # armeabi, NOT armeabi-v7a!

Además, incluso si se especifican ambas ABI ,armeabi y armeabi-v7a, (como se describe a continuación en la sección Declaring Supported ABIs [Declaración de ABI admitidas]), Xamarin.Android creará el siguiente elemento en . csproj:

<AndroidSupportedAbis>armeabi,armeabi-v7a</AndroidSupportedAbis>

Como consecuencia, armeabilibmonodroid.so se encontrará primero en el archivo .apk y armeabilibmonodroid.so será el que se extraiga, aunque armeabi-v7alibmonodroid.so exista y esté optimizado para el destino. Esta situación puede dar también lugar a extraños errores en tiempo de ejecución, dado que armeabi no es seguro para SMP.

Instalación de bibliotecas nativas: Android 4.0.4 y versiones posteriores

Android 4.0.4 cambia la lógica de extracción: se enumeran todas las bibliotecas nativas, se lee el nombre base del archivo y luego se extrae la versión de la API principal o la ABI secundaria (la que sea que exista). Esto permite un comportamiento de "combinación"; es decir, si tenemos un archivo .apk con el siguiente contenido:

lib/armeabi/libone.so
lib/armeabi/libtwo.so
lib/armeabi-v7a/libtwo.so

Tras la instalación, el directorio de biblioteca nativa contendrá:

$APP/lib/libone.so # from armeabi
$APP/lib/libtwo.so # from armeabi-v7a

Xamarin.Android y las ABI

Xamarin.Android admite las siguientes arquitecturas de 64 bits:

  • arm64-v8a
  • x86_64

Nota:

A partir de agosto de 2018 se necesitarán nuevas aplicaciones para alcanzar el nivel 26 de la API, y a partir de agosto de 2019 se requerirá que las aplicaciones proporcionen versiones de 64 bits además de la versión de 32 bits.

Xamarin.Android admite estas arquitecturas de 32 bits:

  • armeabi ^
  • armeabi-v7a
  • x86

Nota:

^ Desde Xamarin.Android 9.2, armeabi ya no se admite.

Xamarin.Android no proporciona actualmente compatibilidad con mips.

Declaración de las ABI admitidas

De forma predeterminada, Xamarin.Android adopta armeabi-v7a para las compilaciones de versión y armeabi-v7a y x86 para las compilaciones de depuración. La compatibilidad con distintas ABI se puede establecer mediante las opciones de proyecto de un proyecto de Xamarin.Android. En Visual Studio, esto se puede establecer en la página Opciones de Android de Propiedades del proyecto, en la pestaña Avanzadas, tal y como se muestra en la siguiente captura de pantalla:

Android Options Advanced properties

En Visual Studio para Mac, las arquitecturas admitidas se pueden seleccionar en la página Compilación de Android de Opciones del proyecto, en la pestaña Avanzadas, como se muestra en la siguiente captura de pantalla:

Android Build Supported ABIs

Existen algunas situaciones en las que puede ser necesario declarar compatibilidad adicional con ABI, por ejemplo:

  • Al implementar la aplicación en un dispositivo x86.

  • Al implementar la aplicación en un dispositivo armeabi-v7a para garantizar la seguridad para subprocesos.

Resumen

En este documento se tratan las distintas arquitecturas de CPU en las que se puede ejecutar una aplicación Android. Se introduce la Interfaz binaria de aplicación y cómo se usa en Android para admitir arquitecturas de CPU dispares. Luego, se pasa a describir cómo especificar la compatibilidad con ABI en una aplicación Xamarin.Android y se resaltan los problemas que surgen al utilizar aplicaciones Xamarin.Android en un dispositivo armeabi-v7a que está pensado solo para armeabi.