Многоядерные устройства и Xamarin.Android

ОС Android может работать в разных компьютерных архитектурах. В этом документе рассматриваются варианты архитектуры ЦП, в которых допускается выполнение приложения Xamarin.Android. Также в этом документе описываются принципы упаковки приложений Android для поддержки нескольких архитектур ЦП. Мы познакомим вас с двоичным интерфейсом приложений (ABI) и подскажем, какие интерфейсы ABI следует применять в приложениях Xamarin.Android.

Обзор

В ОС Android можно создавать "толстые" двоичные файлы с расширением .apk, каждый из которых содержит машинный код для работы в разных архитектурах ЦП. В этих файлах каждый фрагмент машинного кода сопоставлен с определенным двоичным интерфейсом приложений. Интерфейс ABI определяет, какой конкретно фрагмент машинного кода будет выполняться на конкретном физическом устройстве. Например, чтобы приложение Android работало на устройстве x86, при сборке приложения необходимо включить поддержку ABI x86.

Это означает, что каждое приложение Android поддерживает по крайней мере один двоичный интерфейс внедренных приложений (EABI). EABI — это соглашения, специально созданные для внедренного программного обеспечения. Типичный интерфейс EABI описывает следующие форматы:

  • набор инструкций ЦП;

  • порядок следования байтов для операций загрузки и сохранения в памяти во время выполнения;

  • двоичный формат объектных файлов и библиотек программ, а также допустимые и поддерживаемые типы содержимого для этих файлов и библиотек;

  • различные соглашения о передаче данных между кодом приложения и системой (например, порядок применения регистров и (или) стека при вызове функций, ограничения выравнивания и т. д.);

  • ограничения выравнивания и размера для типов перечисления, структур, полей и массивов;

  • список символов функций, доступных в машинном коде во время выполнения, обычно для конкретного узкого набора библиотек.

Архитектура armeabi и потокобезопасность

Двоичный интерфейс приложений подробно описан ниже. Пока мы лишь напомним, что используемая Xamarin.Android среда выполнения armeabiне является потокобезопасной. Если приложение, поддерживающее armeabi, развертывается на устройстве armeabi-v7a, возникает множество странных и порой необъяснимых исключений.

Из-за ошибки, существующей в Android версий 4.0.0, 4.0.1, 4.0.2 и 4.0.3, собственные библиотеки всегда извлекаются из каталога armeabi, даже если присутствует каталог armeabi-v7a и устройство имеет архитектуру armeabi-v7a.

Примечание.

Xamarin.Android следит за тем, чтобы .so добавлялись в APK в правильном порядке. Эта ошибка не должна составлять проблему для пользователей Xamarin.Android.

Описания интерфейсов ABI

Каждый ABI, поддерживаемый в Android, имеет уникальное имя.

armeabi;

Это имя EABI для ЦП с архитектурой ARM, поддерживающих по меньшей мере набор инструкций ARMv5TE. Android соблюдает ABI для ARM GNU/Linux с прямым порядком байтов. Этот интерфейс ABI не поддерживает вычисления с плавающей запятой с использованием аппаратуры. Все операции с плавающей запятой выполняются вспомогательными программными функциями, которые собраны в статическую библиотеку libgcc.a компилятора. В armeabi не поддерживаются устройства SMP.

Внимание

Код armeabi в Xamarin.Android не является потокобезопасным. Его нельзя использовать на многопроцессорных устройствах armeabi-v7a (как описано ниже). Код armeabi можно безопасно использовать на одноядерных устройствах armeabi-v7a.

armeabi-v7a

Это другой набор инструкций для ЦП с архитектурой ARM, дополняющий описанный выше интерфейс EABI armeabi. В EABI armeabi-v7a реализована поддержка операций с плавающей запятой с использованием аппаратуры и поддержка многоядерных устройств (SMP). Приложение, в котором используется EABI armeabi-v7a, может выполняться существенно быстрее, чем приложение с armeabi.

Примечание.

Машинный код armeabi-v7a не выполняется на устройствах ARMv5.

arm64-v8a

Это набор 64-разрядных инструкций, основанный на архитектуре ЦП ARMv8. Эта архитектура используется в устройствах Nexus 9. В Xamarin.Android 5.1 реализована поддержка этой архитектуры (дополнительные сведения см. в разделе о поддержке 64-разрядной среды выполнения).

x86

Это имя ABI для процессоров, поддерживающих набор инструкций, известный как x86 или IA-32. Этот интерфейс ABI соответствует набору инструкций для Pentium Pro, включая наборы для MMX, SSE, SSE2 и SSE3. Он не включает другие необязательные расширения IA-32, такие как:

  • инструкция MOVBE;
  • дополнительное расширение SSE3 (SSSE3);
  • любые варианты SSE4.

Примечание.

Хотя платформа Google TV работает на архитектуре x86, ее не поддерживает Android NDK.

x86_64

Это имя ABI для процессоров, которые поддерживают 64-разрядный набор инструкций x86 (также известен как x64 или AMD64). В Xamarin.Android 5.1 реализована поддержка этой архитектуры (дополнительные сведения см. в разделе о поддержке 64-разрядной среды выполнения).

Формат файлов APK

Пакет приложения Android (APK) — это формат файлов, в котором содержится весь программный код, файлы, ресурсы и сертификаты, которые используются приложением Android. По сути это файл .zip, но с расширением .apk. На снимке экрана ниже в развернутом виде представлено содержимое файла .apk, созданного Xamarin.Android.

Contents of the .apk

Краткое описание содержимого файла .apk.

  • AndroidManifest.xml — это AndroidManifest.xml файл в двоичном формате XML.

  • classes.dex — содержит код приложения, скомпилированный в dex формате файла, используемом виртуальной машиной среды выполнения Android.

  • resources.arsc — этот файл содержит все предварительно скомпилированные ресурсы для приложения.

  • lib — этот каталог содержит скомпилированный код для каждого ABI. Здесь создается отдельная вложенная папка для каждого из интерфейсов ABI, которые мы перечислили в предыдущем разделе. Файл .apk, представленный на снимке экрана выше, содержит собственные библиотеки для armeabi-v7a и x86.

  • META-INF — этот каталог (если он присутствует) используется для хранения сведений о подписи, пакетах и данных конфигурации расширения.

  • res — этот каталог содержит ресурсы, которые не были скомпилированы в resources.arsc .

Примечание.

Файл libmonodroid.so является обязательной собственной библиотекой для всех приложений Xamarin.Android.

Поддержка ABI для устройств Android

Каждое устройство Android поддерживает выполнение машинного кода, соответствующего одному из двух ABI:

  • ABI "primary" — это соответствует коду компьютера, используемому в системном образе.

  • ABI -- это необязательный ABI , который также поддерживается системным образом.

Например, типичное устройство ARMv5TE использует только основной ABI armeabi, а для устройств ARMv7 определен основной ABI armeabi-v7a и дополнительный ABI armeabi. Для устройств x86 обычно указывается только основной ABI x86.

Установка собственной библиотеки Android

Во время установки пакета все собственные библиотеки из .apk извлекаются в каталог собственных библиотек приложения (обычно это /data/data/<package-name>/lib, который далее будет обозначаться как $APP/lib).

Алгоритм установки собственных библиотек Android существенно различается для разных версий Android.

Установка собственных библиотек в Android до версии 4.0

До версии Android 4.0 Ice Cream Sandwich собственные библиотеки извлекались только для одного интерфейса ABI из .apk. Приложения Android тех давних лет сначала пытаются извлечь все собственные библиотеки для основного интерфейса ABI, а если таковых нет, ОС Android извлекает все собственные библиотеки для дополнительного ABI. Слияние не выполняется.

Давайте рассмотрим этот подход на примере приложения, которое устанавливается на устройстве armeabi-v7a. Его файл .apk, поддерживает как armeabi, так и armeabi-v7a, и в его каталоге lib есть следующие каталоги и файлы для ABI:

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 логика извлечения изменилась. Теперь Android просматривает все собственные библиотеки и для каждого файла проверяет, извлечена ли уже библиотека с таким базовым именем и выполняются ли следующие два условия:

  • файл еще не извлечен;

  • интерфейс 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: Galaxy Nexus 4.0.2 использует машинный код armeabi, если в APK есть файлы для armeabi и armeabi-v7a).

Собственные библиотеки обрабатываются "по порядку" (который определяется программой распаковки) и применяется первое соответствие. Так как .apk содержит одновременно версии armeabi и armeabi-v7a для файла libtwo.so, при этом armeabi указан первым, то используется именно версия armeabi, но не правильная версия armeabi-v7a:

$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>

В результате библиотека armeabilibmonodroid.so будет найдена первой в составе .apk, и именно armeabilibmonodroid.so будет извлекаться, даже если в файле присутствует библиотека armeabi-v7alibmonodroid.so, оптимизированная для целевого устройства. Это может приводить к дополнительным неочевидным ошибкам во время выполнения, так как armeabi не поддерживает многоядерные устройства.

Установка собственных библиотек в 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

Примечание.

Начиная с августа 2018 г. новые приложения должны будут использовать API уровня 26, а с августа 2019 г. будет необходимо выпускать 64-разрядные версии приложений в дополнение к 32-разрядным.

Xamarin.Android поддерживает следующие варианты 32-разрядной архитектуры:

  • armeabi ^
  • armeabi-v7a
  • x86

Примечание.

^ Начиная с версии Xamarin.Android 9.2armeabi больше не поддерживается.

Xamarin.Android сейчас не поддерживает mips.

Объявление поддерживаемых ABI

По умолчанию Xamarin.Android применяет armeabi-v7a для сборки выпуска, а armeabi-v7a и x86 — для отладочной сборки. Поддержку других ABI можно задать на странице параметров для проекта Xamarin.Android. В Visual Studio эти значения настраиваются на странице Параметры Android в окне Свойства, на вкладке Дополнительно, как показано на следующем снимке экрана:

Android Options Advanced properties

В Visual Studio для Mac поддерживаемую архитектуру можно выбрать на странице Android Build (Сборка Android) в разделе Project Options (Параметры проекта) на вкладке Advanced (Дополнительно), как показано на следующем снимке экрана:

Android Build Supported ABIs

Существуют ситуации, когда нужно объявить поддержку дополнительного интерфейса ABI, например в следующих случаях:

  • развертывание приложения на устройстве x86;

  • развертывание приложения на устройстве armeabi-v7a с обеспечением потокобезопасности.

Итоги

В этом документе мы рассмотрели архитектуры ЦП, в которых можно запускать приложения Xamarin.Android. Здесь описана концепция двоичного интерфейса приложения и его применение в ОС Android для поддержки разнородных архитектур ЦП. Затем мы объяснили, как правильно описать поддержку интерфейсов ABI в приложении Xamarin.Android, и рассмотрели несколько проблем, которые могут возникать при использовании приложения Xamarin.Android на устройстве armeabi-v7a, которое предназначено только для armeabi.