Multi-Core-Geräte und Xamarin.Android

Android kann in mehreren verschiedenen Computerarchitekturen ausgeführt werden. Dieses Dokument beschreibt die verschiedenen CPU-Architekturen, die für eine Xamarin.Android-Anwendung verwendet werden können. In diesem Dokument wird auch erläutert, wie Android-Anwendungen verpackt werden, um verschiedene CPU-Architekturen zu unterstützen. Die Application Binary Interface (ABI) wird vorgestellt, und es wird eine Anleitung bereitgestellt, welche ABIs in einer Xamarin.Android-Anwendung verwendet werden.

Überblick

Android ermöglicht die Erstellung von „Fat Binaries“, einer einzelnen .apk-Datei, die Computercode enthält, der mehrere, unterschiedliche CPU-Architekturen unterstützt. Dies wird erreicht, indem jedem Stück Computercode eine Application Binary Interface (ABI) zugeordnet wird. Die ABI wird verwendet, um zu steuern, welcher Computercode auf einem bestimmten Hardwaregerät ausgeführt wird. Damit eine Android-Anwendung beispielsweise auf einem x86-Gerät ausgeführt werden kann, ist es erforderlich, dass beim Kompilieren der Anwendung die x86-ABI-Unterstützung mit einbezogen wird.

Insbesondere wird jede Android-Anwendung mindestens eine Embedded-Application Binary Interface (EABI) unterstützen. EABIs sind Konventionen, die speziell für eingebettete Softwareprogramme gelten. Eine typische EABI beschreibt z.B. die folgenden Dinge:

  • Den CPU-Befehlssatz.

  • Die Bytereihenfolge der Speicher- und Ladevorgänge des Arbeitsspeichers zur Laufzeit.

  • Das Binärformat von Objektdateien und Programmbibliotheken sowie die Art des Inhalts, der in diesen Dateien und Bibliotheken zulässig ist oder unterstützt wird.

  • Die verschiedenen Konventionen, mit denen Daten zwischen Anwendungscode und dem System übergeben werden (z.B. wie Register und/oder der Stapel beim Aufruf von Funktionen verwendet werden, Ausrichtungbeschränkungen usw.).

  • Ausrichtungs- und Größenbeschränkungen für Enumerationstypen, Strukturen, Felder und Arrays.

  • Die Liste der Funktionssymbole, die Ihrem Computercode zur Laufzeit zur Verfügung stehen, in der Regel aus einem ganz bestimmten, ausgewählten Satz von Bibliotheken.

armeabi und Threadsicherheit

Die Application Binary Interface wird weiter unten im Detail besprochen, aber es ist wichtig, sich daran zu erinnern, dass die von Xamarin.Android verwendete armeabi-Runtime nicht threadsicher ist. Wenn eine Anwendung mit armeabi-Unterstützung auf einem armeabi-v7a-Gerät bereitgestellt wird, treten zahlreiche seltsame und unerklärliche Ausnahmen auf.

Aufgrund eines Fehlers in Android 4.0.0.0, 4.0.1, 4.0.2, 4.0.2 und 4.0.3 werden die nativen Bibliotheken aus dem Verzeichnis armeabi übernommen, obwohl ein Verzeichnis armeabi-v7a vorhanden ist und das Gerät ein armeabi-v7a-Gerät ist.

Hinweis

Xamarin.Android stellt sicher, dass .so-Dateien dem APK in der richtigen Reihenfolge hinzugefügt werden. Dieser Fehler sollte für Benutzer von Xamarin.Android kein Problem darstellen.

ABI-Beschreibungen

Jede von Android unterstützte ABI wird durch einen eindeutigen Namen identifiziert.

armeabi

Dies ist der Name einer EABI für ARM-basierte CPUs, die mindestens den ARMv5TE-Befehlssatz unterstützen. Android folgt der Little-Endian-ARM-GNU-/Linux-ABI. Diese ABI unterstützt keine hardwareunterstützten Gleitkommaberechnungen. Alle FP-Vorgänge werden von Softwarehilfsfunktionen ausgeführt, die aus der statischen libgcc.a-Bibliothek des Compilers stammen. SMP-Geräte werden von armeabi nicht unterstützt.

Wichtig

Der armeabi-Code von Xamarin.Android ist nicht threadsicher und sollte nicht auf armeabi-v7a-Geräten mit mehreren CPUs verwendet werden (siehe Beschreibung weiter unten). Das Verwenden von armeabi-Code auf einem armeabi-v7a-Einzelkerngerät ist sicher.

armeabi-v7a

Dies ist ein weiterer ARM-basierter CPU-Befehlssatz, der die oben beschriebene armeabi-EABI erweitert. Die armeabi-v7a-EABI unterstützt Hardware-Gleitkommavorgänge und mehrere CPU-Geräte (SMP). Eine Anwendung, die die armeabi-v7a-EABI verwendet, kann erhebliche Leistungsverbesserungen gegenüber einer Anwendung erwarten, die armeabi verwendet.

Hinweis

armeabi-v7a-Computercode kann auf ARMv5 Geräten nicht ausgeführt werden.

arm64 v8a

Dies ist ein 64-Bit-Befehlssatz, der auf der ARMv8-CPU-Architektur basiert. Diese Architektur wird im Nexus 9 verwendet. In Xamarin.Android 5.1 wurde Unterstützung für diese Architektur eingeführt (weitere Informationen finden Sie unter Unterstützung für die 64-Bit-Runtime).

x86

Dies ist der Name einer ABI für CPUs, die den Befehlssatz unterstützen, der üblicherweise x86 oder IA-32 genannt wird. Diese ABI entspricht den Anweisungen für den Pentium Pro-Befehlssatz, einschließlich der MMX-, SSE-, SSE-, SSE2- und SSE3-Befehlssätze. Sie enthält keine weiteren optionalen IA-32-Befehlssatzerweiterungen wie:

  • Die MOVBE-Anweisung.
  • Zusätzliche SSE3-Erweiterung (SSSE3).
  • Eine Variante von SSE4.

Hinweis

Google TV wird vom Android NDK nicht unterstützt, obwohl die Anwendung unter x86 ausgeführt wird.

x86_64

Dies ist der Name einer ABI für CPUs, die den 64-Bit-x86-Befehlssatz unterstützen (auch als x64 oder AMD64 bezeichnet). In Xamarin.Android 5.1 wurde Unterstützung für diese Architektur eingeführt (weitere Informationen finden Sie unter Unterstützung für die 64-Bit-Runtime).

APK-Dateiformat

Das APK-Format (Android Application Package) ist das Dateiformat, das den gesamten Code, die Objekte, Ressourcen und Zertifikate enthält, die für eine Android-Anwendung erforderlich sind. Es handelt sich um eine .zip-Datei, die jedoch die Dateinamenerweiterung .apk verwendet. Der Inhalt einer .apk-Datei (nach der Erweiterung), die von Xamarin.Android erstellt wurde, wird im Screenshot unten gezeigt:

Contents of the .apk

Kurze Beschreibung des Inhalts der .apk-Datei:

  • AndroidManifest.xml – Dies ist die AndroidManifest.xml Datei im binär-XML-Format.

  • classes.dex – Dies enthält den Anwendungscode, der in das dex Dateiformat kompiliert wird, das von der Android-Runtime-VM verwendet wird.

  • resources.arsc – Diese Datei enthält alle vorkompilierten Ressourcen für die Anwendung.

  • lib – Dieses Verzeichnis enthält den kompilierten Code für jede ABI. Es enthält einen Unterordner für jede ABI, die im vorherigen Abschnitt beschrieben wurde. Im obigen Screenshot besitzt die fragliche .apk-Datei native Bibliotheken für armeabi-v7a und für x86.

  • META-INF – Dieses Verzeichnis (sofern vorhanden) wird verwendet, um Signaturinformationen, Paket- und Erweiterungskonfigurationsdaten zu speichern.

  • res – Dieses Verzeichnis enthält die Ressourcen, die nicht kompiliert resources.arsc wurden.

Hinweis

Die Datei libmonodroid.so ist die native Bibliothek, die von allen Xamarin.Android-Anwendungen verlangt wird.

ABI-Unterstützung für Android-Geräte

Jedes Android-Gerät unterstützt die Ausführung von nativem Code in bis zu zwei ABIs:

  • Die "primäre" ABI – Dies entspricht dem Computercode, der im Systemimage verwendet wird.

  • Eine "sekundäre" ABI – Dies ist eine optionale ABI, die auch vom Systemimage unterstützt wird.

Beispielsweise verfügt ein typisches ARMv5TE-Gerät nur über eine primäre ABI von armeabi, während ein ARMv7-Gerät eine primäre ABI von armeabi-v7a und eine sekundäre ABI von armeabi angeben würde. Ein typisches x86-Gerät würde nur eine primäre ABI von x86 angeben.

Installation der nativen Android-Bibliothek

Bei der Paketinstallation werden native Bibliotheken aus der .apk-Datei in das native Bibliotheksverzeichnis der App extrahiert (in der Regel /data/data/<package-name>/lib) und anschließend als $APP/lib bezeichnet.

Das Installationsverhalten der nativen Bibliothek von Android unterscheidet sich erheblich zwischen den verschiedenen Android-Versionen.

Installieren nativer Bibliotheken: Vor Android 4.0

Android vor 4.0 Ice Cream Sandwich extrahiert nur native Bibliotheken aus einer einzigen ABI in der .apk-Datei. Diese alten Android-Anwendungen werden zunächst versuchen, alle nativen Bibliotheken für die primäre ABI zu extrahieren. Wenn keine solchen Bibliotheken vorhanden sind, extrahiert Android dann alle nativen Bibliotheken für die sekundäre ABI. Es erfolgt kein „Mergevorgang“.

Betrachten Sie zum Beispiel eine Situation, in der eine Anwendung auf einem armeabi-v7a-Gerät installiert ist. Die .apk,-Datei, die sowohl armeabi als auch armeabi-v7a unterstützt, verfügt über die folgenden ABI lib-Verzeichnisse und darin enthaltenen Dateien:

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

Nach der Installation enthält das Verzeichnis für die native Bibliothek Folgendes:

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

Das heißt, es wird keine Datei libone.so installiert. Dies führt zu Problemen, da libone.so nicht vorhanden ist, um von der Anwendung zur Laufzeit geladen zu werden. Dieses Verhalten (obwohl unerwartet) wurde als Fehler protokolliert und als „bestimmungsgemäße Funktion“ neu klassifiziert.

Wenn das Ziel Android-Versionen vor 4.0 sind, ist es daher erforderlich, alle nativen Bibliotheken für jede ABI bereitzustellen, die von der Anwendung unterstützt werden, d.h. die .apk-Datei sollte Folgendes enthalten:

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

Installieren nativer Bibliotheken: Android 4.0 – Android 4.0.3

Android 4.0 Ice Cream Sandwich ändert die Extraktionslogik. Es werden alle nativen Bibliotheken aufgezählt, es wird geprüft, ob der Basisname der Datei bereits extrahiert wurde, und wenn beide der folgenden Bedingungen erfüllt sind, wird die Bibliothek extrahiert:

  • Sie wurde nicht bereits extrahiert.

  • Die ABI der nativen Bibliothek stimmt mit der primären oder sekundären ABI des Ziels überein.

Die Erfüllung dieser Bedingungen erlaubt ein „Mergeverhalten“, d.h. wenn eine .apk-Datei mit dem folgenden Inhalt vorhanden ist:

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

Enthält das Verzeichnis der nativen Bibliothek nach der Installation Folgendes:

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

Leider ist dieses Verhalten von der Reihenfolge abhängig, wie im folgenden Dokument beschrieben wird: Problem 24321: Galaxy Nexus 4.0.2 verwendet nativen armeabi-Code, wenn sowohl armeabi als auch armeabi-v7a in der APK-Datei enthalten sind.

Die nativen Bibliotheken werden „in der jeweiligen Reihenfolge“ verarbeitet (z.B. wie durch unzip aufgelistet), und die erste Übereinstimmung wird extrahiert. Da die .apk-Datei die Versionen armeabi und armeabi-v7a von libtwo.so enthält und armeabi zuerst aufgelistet wird, wird die armeabi-Version extrahiert, nicht die armeabi-v7a-Version:

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

Auch wenn die armeabi- und armeabi-v7a-ABIs angegeben werden (wie unten im Abschnitt Deklarieren unterstützter ABIs beschrieben), wird Xamarin.Android das folgende Element in der . csproj:

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

Folglich wird die Datei armeabilibmonodroid.so zuerst in der .apk-Datei gefunden, und armeabilibmonodroid.so wird die Datei sein, die extrahiert wird, obwohl armeabi-v7alibmonodroid.so vorhanden und für das Ziel optimiert ist. Dies kann ebenfalls zu unverständlichen Laufzeitfehlern führen, da armeabi nicht SMP-sicher ist.

Installieren nativer Bibliotheken: Android 4.0.4 oder höher

Android 4.0.4 ändert die Extraktionslogik: Es werden alle nativen Bibliotheken aufgezählt, der Basisname der Datei wird gelesen, dann wird die primäre ABI-Version (wenn vorhanden) oder die sekundäre ABI (wenn vorhanden) extrahiert. Dies erlaubt ein „Mergeverhalten“, d.h. wenn eine .apk-Datei mit dem folgenden Inhalt vorhanden ist:

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

Enthält das Verzeichnis der nativen Bibliothek nach der Installation Folgendes:

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

Xamarin.Android und ABIs

Xamarin.Android unterstützt die folgenden 64-Bit-Architekturen:

  • arm64-v8a
  • x86_64

Hinweis

Ab August 2018 sind neue Apps erforderlich, um auf API-Ebene 26 abzielen, und ab August 2019 sind Apps zum Bereitstellen von 64-Bit-Versionen erforderlich, zusätzlich zu der 32-Bit-Version.

Xamarin.Android unterstützt die folgenden 32-Bit-Architekturen:

  • armeabi ^
  • armeabi-v7a
  • x86

Hinweis

^ Ab Xamarin.Android 9.2 wird armeabi nicht mehr unterstützt.

Xamarin.Android bietet zurzeit keine Unterstützung für mips.

Deklarieren unterstützter ABIs

Standardmäßig verwendet Xamarin.Android armeabi-v7a für Releasebuilds und armeabi-v7a und x86 für Debugbuilds. Unterstützung für verschiedene ABIs kann über die Projektoptionen für ein Xamarin.Android-Projekt festgelegt werden. In Visual Studio kann dies auf der Seite Android-Optionen der Eigenschaften des Projekts auf der Registerkarte Erweitert festgelegt werden, wie im folgenden Screenshot gezeigt:

Android Options Advanced properties

In Visual Studio für Mac können die unterstützten Architekturen auf der Seite Android-Build der Projektoptionen auf der Registerkarte Erweitert ausgewählt werden, wie im folgenden Screenshot gezeigt:

Android Build Supported ABIs

Es gibt einige Situationen, in denen es erforderlich sein kann, zusätzliche ABI-Unterstützung zu deklarieren, z.B. unter den folgenden Umständen:

  • Bereitstellen der Anwendung auf einem x86-Gerät.

  • Bereitstellen der Anwendung auf einem armeabi-v7a-Gerät, um Threadsicherheit zu gewährleisten.

Zusammenfassung

In diesem Dokument wurden die verschiedenen CPU-Architekturen beschrieben, in denen eine Android-Anwendung ausgeführt werden kann. Dabei wurde die Application Binary Interface vorgestellt und gezeigt, wie sie von Android verwendet wird, um unterschiedliche CPU-Architekturen zu unterstützen. Anschließend wurde erläutert, wie die ABI-Unterstützung in einer Xamarin.Android-Anwendung angegeben werden kann und welche Probleme bei der Verwendung von Xamarin.Android-Anwendungen auf einem armeabi-v7a-Gerät auftreten, die nur für armeabi bestimmt sind.