マルチコア デバイスと Xamarin.Android
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
デバイスで使用するときに発生する問題を明らかにしました。