Многоядерные устройства и 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.
Краткое описание содержимого файла .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>
В результате библиотека armeabi
libmonodroid.so
будет найдена первой в составе .apk
, и именно armeabi
libmonodroid.so
будет извлекаться, даже если в файле присутствует библиотека armeabi-v7a
libmonodroid.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 в окне Свойства, на вкладке Дополнительно, как показано на следующем снимке экрана:
В Visual Studio для Mac поддерживаемую архитектуру можно выбрать на странице Android Build (Сборка Android) в разделе Project Options (Параметры проекта) на вкладке Advanced (Дополнительно), как показано на следующем снимке экрана:
Существуют ситуации, когда нужно объявить поддержку дополнительного интерфейса ABI, например в следующих случаях:
развертывание приложения на устройстве
x86
;развертывание приложения на устройстве
armeabi-v7a
с обеспечением потокобезопасности.
Итоги
В этом документе мы рассмотрели архитектуры ЦП, в которых можно запускать приложения Xamarin.Android. Здесь описана концепция двоичного интерфейса приложения и его применение в ОС Android для поддержки разнородных архитектур ЦП.
Затем мы объяснили, как правильно описать поддержку интерфейсов ABI в приложении Xamarin.Android, и рассмотрели несколько проблем, которые могут возникать при использовании приложения Xamarin.Android на устройстве armeabi-v7a
, которое предназначено только для armeabi
.