Сборка приложения в Unity3d: gradle vs internal

Содержание:

Unity3d замечательный инструмент предназначенный для создания игровых приложений. Большая часть повседневных задач в нем автоматизирована. Если создание приложений для вас хобби, тогда нет нужды изобретать колесо. Возьмите Unity3d + 3ds Max, или Blender 3d. Однако тут есть некоторые подводные камни о которых поговорим. Я искренне надеюсь, что моя статья поможет начинающим энтузиастам.

Не так давно, мне понадобилось собрать код своего приложение на другой машине. Я установил последнюю на тот момент версию Unity 2019.1. Взял свой предыдущий проект в Unity 5.6, который я так и не решился опубликовать на Google Play. Открыл и увидел ошибку «Prefab is missing». Я внимательно проверил все папки в моем проекте и все 3d-модели были на своем месте. Этот факт очень меня удивил.

Я подумал что причина в том что я изменил версию. Для проверки этого предположения я установил такую же версию (2019.1) на свою машину и не увидел: «Prefab is missing». Причина в том что Unity3d поддерживает 3d-модели которые созданы в Blender3d только когда он установлен. В нем нет встроенной поддержки Blender-моделей. Я установил этот пакет, но ошибка не исчезла. И это меня ещё больше удивило.

В документации сказано что Unity3d все ресурсы кеширует и потом производит над ними необходимые операции. В кеш ресурсы попадают после импорта. Я осуществил повторный импорт ресурсов (Assets->Reimport All) и ошибка исчезла.

Автоматизированные системы сборки

Для сборки приложения не достаточно исходного кода. Необходимо указать директивы для правильной линковки всех компонентов. В Unity все динамические библиотеки помещаются в папку Assets/Plugins. Самые трудно обнаруживаемые ошибки происходят во время выполнения приложения. Если в управляемом коде ошибки обнаруживаются быстро, то для обнаружения ошибок в нативном коде требуется выполнение на целевой платформе.

Отсутствующие ссылки на требуемые функции в нативных библиотеках приведут к ошибкам во время выполнения приложения. Таким образом, при сборке приложения ошибок вы не увидите. Но во время выполнения требуемая функция не будет найдена и программа остановиться. Это не критично, но неприятно и раздражает пользователей.

Приложения это сжатые в формате zip «.aab» и «.apk» файлы. Автоматизированные системы сборки отвечают за формирования таких файлов. Содержимое данных файлов зависит от настроек системы сборки. По умолчанию в Unity3d использует свою внутреннюю (Internal) систему сборки. Данная система сборки позволяет формировать только apk файлы и автоматически включает все файлы находящиеся в каталоге Assets/Plugins в проект.

Недостатки внутренней автоматизированной системы сборки

Давайте представим что вы очень давно создали проект своего приложения. Вы следите за тенденциями в компьютерном мире и безопасностью в целом. Вы постоянно обновляете компоненты своего приложения. Это правильно и так должно быть.

Предположим, что ваш проект содержит библиотеку функций «mylib-1.0.dll», которая ссылается на функции библиотеки «libxml.so». Через два года работы вашего приложения вы узнали что появилась новая версия «mylib-2.1.dll». Новая версия для своей работы требует уже не «libxml.so», а «libcsv.so». Исключив из своего приложения libxml.so вы ничего не заметите, потому что в версии «2.1» вашей библиотеки он не используется. Тем не менее, если вы забудете исключить этот файл из проекта, тогда автоматизированная система сборки включит эту вещь в проект.

Относительно «.jar» и «.aar» файлов в папке Plugins действуют подобные принципы. Все файлы классов «.class» включаются в проект. Не имеет значения нужны они или нет. Внутренняя автоматизированная система сборки включит весь набор классов. Это удобно, но только для новичков. Которые не хотят вникать в тонкости сборки программных пакетов.

Способы устранения устаревших зависимостей

Одним из способов борьбы с устаревшими зависимостями является хорошая документация проекта. Документация на разработанный программный продукт должна включать морфологическую карту для каждой версии приложения.

Морфологическая карта для двух версий приложения

Каждая новая версия библиотеки содержит changelog.txt в котором содержится список изменений по версиям приложения. Однако если в проекте слишком много сторонних зависимостей, тогда прочтение огромного количества информации может изнурить вас.

Поскольку со временем сторонние зависимости могут изменить свой функционал рекомендуют составлять unit-тесты. Это проще чем читать огромное количество документации. Впрочем, читать документацию придется, но не очень много.

Как получить список методов в библиотеке?

Самый простой способ использовать обозреватель решений, справа в окне Microsoft Visual Studio. Для этого кликните по одному из объектов обозревателя решений, например «System.Net.Security.dll», и нажмите «Просмотр в обозревателе объектов».

System.Net.Security.dll
Библиотека содержащая методы шифрования

В обозревателе объектов вы увидите список всех доступных классов, с небольшой справкой. Таким образом, вы можете просмотреть какой функционал реализует библиотека.

KeyAgreeRecipientInfo
Свойство «OriginatorIdentifierOrKey» в классе содержащее информацию об инициаторе согласования ключей

Вы можете узнать информацию о классах, полях, методах библиотек .Net во время выполнения своего приложения. Список импортируемых методов из managed библиотек можно получить применив System.Reflection. В этом namespace присутствует класс MethodInfo. Для получения информации о методах входящих в класс «MyClass» из динамической managed библиотеки:

MethodInfo[] methodInfos = typeof(MyClass).GetMethods(BindingFlags.Public |
                                                      BindingFlags.Static);

GetMethods из пространства имен «System.Reflection» использует и Visual Studio. Но что если вам захочется увидеть список функций в нативных библиотеках. В таблице ниже написаны средства для разных динамических библиотек которые написаны для разных ОС [1].

ОСПакетПрограмма
Linuxbinutilsobjdump -T MyLib.so
WindowsVisual Studiodumpbin /exports MyLib.dll
MacOSbinutilsobjdump -T MyLib.so
Androidbinutilsobjdump -T libMyLib.so

Отличия нативных динамических двоичных файлов

Для каждой из аппаратных архитектур используются свои кросс-компиляторы и утилиты из пакета binutils. Так название кросс-компилятора gcc для архитектуры arm-v7a будет выглядеть как: armv7a-linux-androideabi-gcc. А для архитектуры arm-v8a: aarch64-linux-android.

Утилита «file» в Linux может показать файловые заголовки. Давайте посмотрим, чем отличается динамическая shared object библиотека для Linux от динамической библиотеки для Android. Выполните в командной строке Linux:

[Cubby dev@machine src]$ file libMyLib.so
libMyLib.so: ELF 64-bit LSB shared object,
ARM aarch64, version 1 (SYSV), dynamically linked,
interpreter /system/bin/linker64,
BuildID[sha1]=45b9548a4d5bed6f702ff1a2af5208ad6aec312b,
with debug_info, not stripped

Обратите внимание на расположение linker64. Если вместо директории /system/bin вы видите что-то иное, например /lib, тогда данная библиотека собрана не для Android. В Linux большая часть исполняемых файлов находится в директории /usr/bin, а библиотеки в /usr/lib. В Linux применяется стандартная библиотека языка Си libc, или uClibc для встраиваемых систем. В Android своя стандартная библиотека, которая называется Bionic. Единственная схожесть состоит в формате исполняемых файлов ELF [2].

Преимущества Gradle и Proguard оптимизация

Выберите Build system: Gradle вместо Internal. Теперь вы можете собирать приложения в файлах aab. Для этого поставьте галочку напротив «Build App Bundle».

gradle
Сборка Unity3d проектов

Использование Gradle и Proguard позволят уменьшить размер исполняемого файла. Однако, есть некоторые нюансы на которые следует обратить внимание. Особенно если у вас есть плагины для Android (.aar), или Jar-библиотеки. Приложение иногда можно собрать и запустить без проблем, но во время выполнения могут возникать ошибки. В таких случаях необходимо через USB подключить устройство к вашему PC и запустить отладку вашего приложения через adb следующим образом:

adb logcat -d com.your_company.your_app:I *:W

Вы можете перенаправить вывод в файл добавив «> warnings.txt». Это помогает найти нужную информацию. Впрочем, поступайте как вам удобно. Adb входит в состав android sdk и доступен для Windows, MacOS и Linux. Если после сборки при помощи Gradle вы видите ошибку ClassNotFoundException, тогда вам надо проверить наличие в aar или jar библиотеках наличие данного класса. Если он есть но ошибка не исчезает, тогда поставьте галочку напротив User Proguard File.

User proguard file

Предположим, что ошибка: java.lang.ClassNotFoundException: > Didn’t find class > «com.google.android.gms.ads.MobileAds». Но в папочке Asset/Plugins/Android есть файл com.google.android.gms.ads.jar в этом архиве присутствует файл «MobileAds.class». Чтобы включить этот класс откройте файл proguard-user.txt в любом редакторе и добавьте следующие строчки:

-keep class com.google.android.gms.ads.** {
*;
}

Сравнение apk и aab пакетов

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

Файлы aab, как холодильник хранят в себе все компоненты для бургера. Если у вашего устройства небольшой dpi, вы не будете качать ресурсы с большим dpi. Вы возьмете только то что вам надо. Но вы же, не будете есть холодный кусок говядины. Его надо разогреть! Подобным образом устроен и aab файл. После того как все необходимые компоненты скачаны, они компонуются в apk на устройствах клиентов [3].

Proguard и обфускация методов

Самое большое преимущество, это обфускация полей и методов в классах. В релизе вы будите иметь, компактный бинарный файл без ничего лишнего. Алгоритм Бойера-Мура для эффективного поиска работает быстрее при нормальном распределении букв в названиях методов. Это должно привести к быстрой работе приложения.

Поэтому unity и многие движки имеют дело не с названиями ресурсов, а с неудобочитаемым хешкодом вида: «ab103628e47f1da0bcd». Gradle переопределяет название методов и таким образом ускоряется нахождения функции. После выполнения функции она попадает в кеш и при повторном вызове она выполнится ещё быстрее. Если вы еще не используете proguard подумайте об этом.

Выводы

Встроенная в Unity3d автоматизированная система сборки подходит начинающим. Для опытных пользователей рекомендуется использовать Gradle. Основное преимущество Gradle — возможность создавать aab файлы. Вместе с Gradle используется proguard оптимизация переопределяющая названия функций в классах. Это увеличивает отзывчивость вашего приложения. Для включения классов из aar и jar библиотек, необходимо внести их названия в файл proguard-user.txt.

Источники информации:

[1] https://developer.android.com/ndk/guides/other_build_systems
[2] https://android.googlesource.com/platform/bionic
[3] https://developer.android.com/guide/app-bundle

Оставьте комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *