Android は、複数の異なるコンピューター アーキテクチャで実行できます。 このドキュメントでは、Xamarin.Android アプリケーションに利用できるさまざまな CPU アーキテクチャについて説明します。 このドキュメントでは、さまざまな CPU アーキテクチャをサポートするために Android アプリケーションがどのようにパッケージ化されているかについても説明します。 アプリケーション バイナリ インターフェイス (ABI) が導入され、Xamarin.Android アプリケーションで使用する ABI に関するガイダンスが提供される予定です。
概要
Android では、"FAT バイナリ" を作成することができます。これは、複数の異なる CPU アーキテクチャをサポートするマシン コードを含む 1 つの .apk
ファイルです。 これは、マシン コードの各部分をアプリケーション バイナリ インターフェイスと関連付けることで行います。 ABI は、特定のハードウェア デバイスでどのマシン コードを実行するかを制御するために使用されます。 たとえば、x86 デバイス上で実行する Android アプリケーションには、アプリケーションのコンパイル時に x86 ABI のサポートを含める必要があります。
具体的には、各 Android アプリケーションは 1 つ以上の埋め込みアプリケーション バイナリ インターフェイス (EABI) をサポートします。 EABI とは、埋め込みソフトウェア プログラムに固有の規則です。 一般的な EABI は次のようなことを記述します。
MMX 命令セット。
実行時に格納および読み込むメモリのエンディアン。
オブジェクト ファイルとプログラム ライブラリのバイナリ形式、およびこれらのファイルとライブラリで許可またはサポートされるコンテンツの種類。
アプリケーション コードとシステム間でデータを渡すために使用されるさまざまな規則 (例: 関数が呼び出されるときのレジスタまたはスタックの使用方法、配置制約など)。
列挙型、構造体、フィールド、および配列の配置とサイズの制約。
実行時にマシン コードで使用できる (通常は厳選されたライブラリのセットからの) 関数シンボルの一覧。
armeabi とスレッド セーフ
アプリケーション バイナリ インターフェイスについては以降で詳しく説明しますが、Xamarin.Android で使用される armeabi
ランタイムは、スレッド セーフではないことに注意してください。 armeabi
をサポートしているアプリケーションを armeabi-v7a
デバイスに展開する場合、変わった予期しない例外が数多く発生します。
Android 4.0.0、4.0.1、4.0.2、および 4.0.3 のバグにより、armeabi-v7a
ディレクトリが存在し、デバイスが armeabi-v7a
デバイスであっても、armeabi
からネイティブ ライブラリが取得されます。
Note
Xamarin.Android では、.so
が正しい順序で APK に追加されます。 このバグは Xamarin.Android のユーザーにとっては問題にはなりません。
ABI の説明
Android でサポートされている各 ABI は、一意の名前で識別されます。
armeabi
これは、最低限 ARMv5TE 命令セットをサポートする ARM ベースの CPU の EABI の名前です。 Android では、リトル エンディアン ARM GNU/Linux ABI に従います。 この ABI は、ハードウェア依存の浮動小数点演算をサポートしていません。 すべての FP 演算は、コンパイラの libgcc.a
スタティック ライブラリに由来するソフトウェア ヘルパー関数で実行されます。 SMP デバイスは armeabi
でサポートされていません。
重要
Xamarin.Android の armeabi
コードはスレッド セーフではないため、マルチ CPU の armeabi-v7a
デバイスでは使用しないでください (以下で説明)。 シングル コアの armeabi-v7a
デバイスで armeabi
コードを使用するのは安全です。
armeabi-v7a
これは、上記で説明した armeabi
EABI を拡張する別の ARM ベースの CPU 命令セットです。 armeabi-v7a
EABI は、ハードウェアの浮動小数点演算と複数の CPU (SMP) デバイスをサポートしています。 armeabi-v7a
EABI を使用するアプリケーションは、armeabi
を使用するアプリケーションに比べて大幅なパフォーマンスの向上が期待できます。
Note
armeabi-v7a
マシン コードは、ARMv5 デバイスでは実行できません。
arm64-v8a
これは、ARMv8 CPU アーキテクチャに基づく 64 ビット命令セットです。 このアーキテクチャは、Nexus 9 で使用されています。 Xamarin.Android 5.1 ではこのアーキテクチャのサポートが導入されています (詳細については「64-bit runtime support (64 ビット ランタイムのサポート)」を参照)。
x86
これは、一般的な x86 または IA-32 という名前の命令セットをサポートする CPU の ABI の名前です。 この ABI は、Pentium Pro 命令セット (MMX、SSE、SSE2、および SSE3 の命令セットを含む) の命令に対応します。 次のような、その他のオプションの IA-32 命令セットの拡張機能は含まれません。
- MOVBE 命令。
- 追加の SSE3 拡張機能 (SSSE3)。
- SSE4 の任意のバリアント。
Note
Google TV は x86 上で実行されますが、Android の NDK ではサポートされていません。
x86_64
これは、64 ビット x86 命令セット (x64 または AMD64 とも呼ばれます) をサポートする CPU の ABI の名前です。 Xamarin.Android 5.1 ではこのアーキテクチャのサポートが導入されています (詳細については「64-bit runtime support (64 ビット ランタイムのサポート)」を参照)。
APK ファイル形式
Android アプリケーション パッケージは、Android のアプリケーションに必要なコード、アセット、リソース、および証明書をすべて保持するファイル形式です。 これは .zip
ファイルですが、.apk
ファイル名拡張子を使用します。 展開すると、次のスクリーンショットのような、Xamarin.Android によって作成された .apk
のコンテンツが表示されます。
.apk
ファイルのコンテンツを簡単に説明します。
AndroidManifest.xml – バイナリ XML 形式の
AndroidManifest.xml
ファイルです。classes.dex – Android ランタイム VM によって使用される
dex
ファイル形式にコンパイルされたアプリケーション コードが含まれます。resources.arsc – このファイルには、アプリケーションのプリコンパイル済みリソースがすべて含まれています。
lib – このディレクトリには、各 ABI のコンパイル済みコードが保持されています。 前のセクションで説明した各 ABI に対して 1 つのサブフォルダーが格納されます。 上記のスクリーンショットでは、該当する
.apk
にはarmeabi-v7a
とx86
の両方のネイティブ ライブラリがあります。META-INF – このディレクトリ (存在する場合) は、署名情報、パッケージ、および拡張機能の構成データを格納するために使用されます。
res – このディレクトリには、
resources.arsc
にコンパイルされなかったリソースが保持されます。
Note
ファイル libmonodroid.so
は、すべての Xamarin.Android アプリケーションで必要なネイティブ ライブラリです。
Android デバイスの ABI のサポート
各 Android デバイスは、最大 2 つの ABI でネイティブ コードの実行をサポートします。
"プライマリ" ABI – これは、システム イメージで使用されるマシン コードに対応します。
"セカンダリ" ABI – これも、システム イメージでサポートされるオプションの ABI です。
たとえば、典型的な ARMv5TE デバイスでは、armeabi
のプライマリ ABI しかないのに対し、ARMv7 デバイスでは、armeabi-v7a
のプライマリ ABI と armeabi
のセカンダリ ABI を指定します。 典型的な x86 デバイスでは、x86
のプライマリ ABI のみを指定します。
Android のネイティブ ライブラリのインストール
パッケージのインストール時に、.apk
内のネイティブ ライブラリがアプリのネイティブ ライブラリのディレクトリに抽出されます。このディレクトリは、通常、/data/data/<package-name>/lib
で、以降は $APP/lib
とします。
Android のネイティブ ライブラリのインストール動作は、Android のバージョンによって大幅に異なります。
ネイティブ ライブラリのインストール: Android 4.0 より前のバージョン
Android 4.0 Ice Cream Sandwich より前のバージョンでは、.apk
内の 1 つの ABI からのみネイティブ ライブラリを抽出します。 この古い Android アプリが最初にプライマリ ABI のすべてのネイティブ ライブラリの抽出を試みて、ライブラリが存在しない場合は、次に Android がセカンダリ ABI のすべてのネイティブ ライブラリを抽出します。 "マージ" は行われません。
たとえば、アプリケーションが armeabi-v7a
デバイスにインストールされている状況を考えてみましょう。 armeabi
と armeabi-v7a
の両方をサポートする .apk,
の中には、次の ABI lib
ディレクトリとファイルがあります。
lib/armeabi/libone.so
lib/armeabi/libtwo.so
lib/armeabi-v7a/libtwo.so
インストール後、ネイティブ ライブラリ ディレクトリに次のものが含まれます。
$APP/lib/libtwo.so # from the armeabi-v7a directory in the apk
つまり、libone.so
はインストールされません。 これは、実行時に読み込むアプリケーションの libone.so
が存在しないため、問題が発生します。 この動作は予期しないものですが、バグとしてログに記録され、"目的どおりに動作" として再分類されています。
その結果、Android 4.0 より前のバージョンを対象とする場合には、アプリケーションがサポートする各 ABI に対して、すべてのネイティブ ライブラリを提供する必要があります。つまり、.apk
を含める必要があります。
lib/armeabi/libone.so
lib/armeabi/libtwo.so
lib/armeabi-v7a/libone.so
lib/armeabi-v7a/libtwo.so
ネイティブ ライブラリのインストール: Android 4.0 – Android 4.0.3
Android 4.0 Ice Cream Sandwich では抽出ロジックが変更されています。 すべてのネイティブ ライブラリが列挙され、ファイルのベース名が既に抽出されているかどうか、および次の両方の条件が満たされているかどうかを確認してから、ライブラリが抽出されます。
まだ抽出されていない。
ネイティブ ライブラリの ABI がターゲットのプライマリまたはセカンダリ ABI と一致している。
これらの条件を満たすと、"マージ" 動作が可能になります。つまり、.apk
と次のコンテンツがある場合、
lib/armeabi/libone.so
lib/armeabi/libtwo.so
lib/armeabi-v7a/libtwo.so
インストール後、ネイティブ ライブラリ ディレクトリに次のものが含まれます。
$APP/lib/libone.so
$APP/lib/libtwo.so
ただし、この動作は、ドキュメント「Issue 24321: Galaxy Nexus 4.0.2 uses armeabi native code when both armeabi and armeabi-v7a is included in apk」 (問題 24321: apk に armeabi と armeabi v7a の両方が含まれていると、Galaxy Nexus 4.0.2 では armeabi ネイティブ コードが使用される) で説明されているように、順序依存です。
ネイティブ ライブラリは、(たとえば、unzip で一覧表示されているように) "順番に" 処理され、最初の一致が抽出されます。 .apk
には libtwo.so
の armeabi
バージョンと armeabi-v7a
バージョンが含まれており、armeabi
が最初にリストされているため、armeabi-v7a
バージョンではなく、armeabi
バージョンが抽出されます。
$APP/lib/libone.so # armeabi
$APP/lib/libtwo.so # armeabi, NOT armeabi-v7a!
さらに、(後述のセクション「サポートされる ABI の宣言」で説明されているように) armeabi
と armeabi-v7a
の両方の ABI が指定されている場合でも、Xamarin.Android では以下に含まれる次の要素が作成されます。
csproj
=
<AndroidSupportedAbis>armeabi,armeabi-v7a</AndroidSupportedAbis>
その結果、armeabi
libmonodroid.so
は最初に .apk
内で見つかり、armeabi-v7a
libmonodroid.so
が存在し、ターゲット用に最適化されていても、抽出されるのは armeabi
libmonodroid.so
になります。 armeabi
が SMP セーフではないため、あいまいな実行時エラーが発生する可能性もあります。
ネイティブ ライブラリのインストール: Android 4.0.4 以降
Android 4.0.4 では、抽出ロジックが変更されています。すべてのネイティブ ライブラリが列挙され、ファイルのベース名の読み取り後、プライマリ ABI バージョン (存在する場合)、またはセカンダリ ABI (存在する場合) が抽出されます。 これにより、"マージ" 動作が可能になります。つまり、.apk
と次のコンテンツがある場合、
lib/armeabi/libone.so
lib/armeabi/libtwo.so
lib/armeabi-v7a/libtwo.so
インストール後、ネイティブ ライブラリ ディレクトリに次のものが含まれます。
$APP/lib/libone.so # from armeabi
$APP/lib/libtwo.so # from armeabi-v7a
Xamarin.Android と ABI
Xamarin.Android では、次の "64 ビット" アーキテクチャがサポートされています。
arm64-v8a
x86_64
Note
2018 年 8 月から、新しいアプリは API レベル 26 をターゲットにすることが必須となります。また、2019 年 8 月から、アプリは 32 ビット バージョンに加えて 64 ビット バージョンを提供することが必須となります。
Xamarin.Android では、次の 32 ビット アーキテクチャがサポートされています。
armeabi
^armeabi-v7a
x86
Note
^Xamarin.Android 9.2 以降、armeabi
はサポート対象から除外されました。
Xamarin.Android では現在、mips
をサポートしていません。
サポートされる ABI の宣言
既定では、Xamarin.Android のリリース ビルドには armeabi-v7a
が既定となり、デバッグ ビルドには armeabi-v7a
と x86
が既定となります。 さまざまな ABI のサポートは、Xamarin.Android プロジェクトのプロジェクト オプションから設定できます。 Visual Studio では、次のスクリーンショットで示すように、これらの値は、プロジェクトの [プロパティ] の [Android オプション] ページの [詳細設定] タブの下で設定できます。
Visual Studio for Mac では、次のスクリーンショットで示すように、サポートされるアーキテクチャは、[プロジェクト オプション] の [Android のビルド] ページの [詳細設定] タブの下で選択できます。
状況によっては、追加の ABI のサポートを宣言する必要があります。たとえば次のような状況が考えられます。
アプリケーションを
x86
デバイスに展開する。スレッド セーフを保証するためにアプリケーションを
armeabi-v7a
デバイスに展開する。
まとめ
このドキュメントでは、Android アプリケーションを実行できるさまざまな CPU アーキテクチャについて説明しました。 アプリケーション バイナリ インターフェイスと、それが異なる CPU アーキテクチャをサポートするために Android でどのように使用されているかについて説明しました。
また、Xamarin.Android アプリケーションでの ABI サポートを指定する方法について説明し、Xamarin.Android アプリケーションを armeabi
のみを対象とした armeabi-v7a
デバイスで使用するときに発生する問題を明らかにしました。