В некоторых случаях необходимо работать над снижением размера сборки для Андроид. Например, установка тяжеловесных APK для пользователей мобильного интернета может влететь в копеечку. Превышение размера APK в 50 Мб в Google Play выливается в дополнительные трудности при аплоаде.

Мы разрабатывали под Андроид на Unity 2D-игру, которая изобилует картинками (большинство с областями прозрачности) и разнообразными звуками, и столкнулись с проблемой размера APK. Забегая вперед скажу, что решив ее и снизив вес в 1,5 раза, мы получили в 1,5 раза больше скачиваний. Заставляет задуматься, не правда ли?
Пытаясь побороть проблему приложений-тяжеловесов, мы обратились к замечательной документации от Unity. Выяснилось, что там данная тема обсуждается лишь вскользь. Упоминается, какие именно аспекты влияют на размер сборки и как воздействовать на эти аспекты, чтобы сделать APK меньше, а также приведено немного конкретики по поводу настроек импорта изображений.
Другую полезную информацию по данному вопросу в интернете мы практически не нашли. Потому провели небольшое исследование, результатами которого спешим с вами поделиться.
Итак, согласно документации, на размер сборки влияет:
С мешами и импортированными анимационными клипами мы не работали, потому начнем с ассетов.
Уточним насчёт изображений. В нашей игре соблюдается принцип pixel-perfect. Все изображения без альфа-канала имеют плавный градиент. Остальные изображения с прозрачностью, а непрозрачные области тоже имеют плавный градиент.
Проанализируем, какой вклад в размер АРК вносят изображения в формате PNG и JPG.
Для PNG этот вклад зависит от двух «переменных»:
Мы сохранили 3 картинки в Photoshop с настройками: Compression->Smallest/Slow, Intrelaced: No. Картинка PNG размером 295 Кбайт заняла в сборке места 236 Кбайт. Картинка 612 Кбайт заняла 480 Кбайт. Картинка 21,2 Кбайт заняла 23 Кбайт (естественно, при одинаковых настройках импорта). Пропроциональность очевидна.
Далее мы сохранили первую картинку в Photoshop с настройками: Compression->None/Fast, Intrelaced: Yes. Получили файл размером 9000 Кбайт, однако в сборку он добавил те же 236 Кбайт.
Это подтверждает слова из документации, о том, что Unity перекодирует все ассеты во внутренний формат. Очевидно, он приблизительно соответствует формату PNG, сохранённому в фотошопе с настройками: Compression->Smallest/Slow, Intrelaced: No. И Unity перекодирует все PNG в этот формат, независимо от начального формата файла.
Опыты однозначно показали, что изображения JPG вносят вклад в размер сборки пропорционально своему размеру. Пока не совсем понятно, как ведёт себя Unity в случае, если ассет находится в формате JPG. Мы сохранили ту же самую (первую) картику в JPG и получили очень маленький файл на файловой системе (75 Кбайт). Однако в сборке он занял непомерных 767 Кбайт (*).
Мы проводили эксперимент на большом количестве разных картинок, здесь я привожу лишь малую часть исследований. В других экспериментах нам наоборот удавалось снизить общий размер сборки конвертированием всех файлов из PNG в JPG.
Причин этого явления выявить пока не удалось.
Скорее всего, снова имеет место внутренний формат Unity, однако точные принципы взаимодействия колдовского механизма Unity с ассетами JPG выяснить пока не удалось.
Еще один способ работы с JPG будет описан ниже.
Выводы
Настройки импорта влияют на размер сборки совершенно так, как это описано в доке (чем качественнее картинка, тем больший её вклад в размер сборки). Здесь скрытых особенностей не выявлено. (С другими форматами изображений эксперименты пока не проводились.)
Для звуков значение имеют две настройки импорта: Audio Format и ForceToMono.
Файлы в формате WAV занимают больше места в сборке, чем файлы в формате MP3 (вклад в размер сборки приблизительно равен размеру файла). Стерео WAV файл займёт меньше места, если выставить ForceToMono = true. Точно такого же эффекта можно достичь, если конвертировать файл в «моно» ещё до импорта (тогда ForceToMono не будет доступна в инспекторе).
Однако если для файла WAV установить настройку импорта AudioFormat = Compressed, то в сборке он займёт места столько же, сколько и соответствующий MP3 файл высшего качества (Audacity: variable bitrate, 220-260 kbps). То есть Unity самостоятельно кодирует звук в формат MP3.
Для файлов в одинаковом формате действует принцип прямо пропорционального вклада в размер сборки.
Остальные настройки импорта на размер сборки никак не влияют. Они влияют на количество места, занимаемое звуком в RAM.
После выполнения каждого пункта желательно проверять полученный эффект. Поскольку в поведении Unity всё же есть доля магии.
Документ приводит список обязательно включаемых в сборку dll и призывает минимизировать количество дополнительных dll (особенно тяжёлых). В частности, по возможности не использовать System.dll (добавляет к APK 2 Мбайт). Однако даже если мы будем избегать ссылок на методы из этой библиотеки, System.dll всё равно (по состоянию на Unity 4.5.5) включается в сборку, так как её подтягивает обязательная Mono.Security.dll. Потому, получается, что официальная документация Unity в этом месте не совсем релевантна.
А вот использования других (необязательных) dll желательно избегать во имя снижения размера сборки.
На сегодня у нас всё. Спасибо за внимание!
(Просьба рассматривать данный материал как более-менее систематизированные результаты исследования, а не как универсальное руководство к действию. Буду очень рад комментариям, замечаниям, конструктивной критике, возражениям, предложениям, дополнениям. И, конечно, несказанно рад буду узнать, какие приёмы применяют коллеги из других компаний для решения обсуждаемого вопроса.)

Мы разрабатывали под Андроид на Unity 2D-игру, которая изобилует картинками (большинство с областями прозрачности) и разнообразными звуками, и столкнулись с проблемой размера APK. Забегая вперед скажу, что решив ее и снизив вес в 1,5 раза, мы получили в 1,5 раза больше скачиваний. Заставляет задуматься, не правда ли?
Пытаясь побороть проблему приложений-тяжеловесов, мы обратились к замечательной документации от Unity. Выяснилось, что там данная тема обсуждается лишь вскользь. Упоминается, какие именно аспекты влияют на размер сборки и как воздействовать на эти аспекты, чтобы сделать APK меньше, а также приведено немного конкретики по поводу настроек импорта изображений.
Другую полезную информацию по данному вопросу в интернете мы практически не нашли. Потому провели небольшое исследование, результатами которого спешим с вами поделиться.
Итак, согласно документации, на размер сборки влияет:
- Размер ассетов (изображения и звуки);
- Меши и анимационные клипы;
- Размер включаемых dll.
С мешами и импортированными анимационными клипами мы не работали, потому начнем с ассетов.
ИЗОБРАЖЕНИЯ
Уточним насчёт изображений. В нашей игре соблюдается принцип pixel-perfect. Все изображения без альфа-канала имеют плавный градиент. Остальные изображения с прозрачностью, а непрозрачные области тоже имеют плавный градиент.
Проанализируем, какой вклад в размер АРК вносят изображения в формате PNG и JPG.
Картинки в формате PNG
Для PNG этот вклад зависит от двух «переменных»:
- Размер картинки на файловой системе;
- Настройка импорта.
Мы сохранили 3 картинки в Photoshop с настройками: Compression->Smallest/Slow, Intrelaced: No. Картинка PNG размером 295 Кбайт заняла в сборке места 236 Кбайт. Картинка 612 Кбайт заняла 480 Кбайт. Картинка 21,2 Кбайт заняла 23 Кбайт (естественно, при одинаковых настройках импорта). Пропроциональность очевидна.
Далее мы сохранили первую картинку в Photoshop с настройками: Compression->None/Fast, Intrelaced: Yes. Получили файл размером 9000 Кбайт, однако в сборку он добавил те же 236 Кбайт.
Это подтверждает слова из документации, о том, что Unity перекодирует все ассеты во внутренний формат. Очевидно, он приблизительно соответствует формату PNG, сохранённому в фотошопе с настройками: Compression->Smallest/Slow, Intrelaced: No. И Unity перекодирует все PNG в этот формат, независимо от начального формата файла.
Картинки в формате JPG
Опыты однозначно показали, что изображения JPG вносят вклад в размер сборки пропорционально своему размеру. Пока не совсем понятно, как ведёт себя Unity в случае, если ассет находится в формате JPG. Мы сохранили ту же самую (первую) картику в JPG и получили очень маленький файл на файловой системе (75 Кбайт). Однако в сборке он занял непомерных 767 Кбайт (*).
Мы проводили эксперимент на большом количестве разных картинок, здесь я привожу лишь малую часть исследований. В других экспериментах нам наоборот удавалось снизить общий размер сборки конвертированием всех файлов из PNG в JPG.
Причин этого явления выявить пока не удалось.
Скорее всего, снова имеет место внутренний формат Unity, однако точные принципы взаимодействия колдовского механизма Unity с ассетами JPG выяснить пока не удалось.
Еще один способ работы с JPG будет описан ниже.
Выводы
- Судя по всему, Unity берет во внимание и формат, в котором сохранено изображение, и характеристики изображения и выполняет сжатие по каким-либо внутренним принципам;
- Один принцип совершенно однозначен: изображения в одинаковом формате вносят вклад в размер сборки, прямо пропорциональный (но НЕ РАВНЫЙ) своему размеру на файловой системе.
Настройки импорта влияют на размер сборки совершенно так, как это описано в доке (чем качественнее картинка, тем больший её вклад в размер сборки). Здесь скрытых особенностей не выявлено. (С другими форматами изображений эксперименты пока не проводились.)
ЗВУКИ
Для звуков значение имеют две настройки импорта: Audio Format и ForceToMono.
Файлы в формате WAV занимают больше места в сборке, чем файлы в формате MP3 (вклад в размер сборки приблизительно равен размеру файла). Стерео WAV файл займёт меньше места, если выставить ForceToMono = true. Точно такого же эффекта можно достичь, если конвертировать файл в «моно» ещё до импорта (тогда ForceToMono не будет доступна в инспекторе).
Однако если для файла WAV установить настройку импорта AudioFormat = Compressed, то в сборке он займёт места столько же, сколько и соответствующий MP3 файл высшего качества (Audacity: variable bitrate, 220-260 kbps). То есть Unity самостоятельно кодирует звук в формат MP3.
Для файлов в одинаковом формате действует принцип прямо пропорционального вклада в размер сборки.
Остальные настройки импорта на размер сборки никак не влияют. Они влияют на количество места, занимаемое звуком в RAM.
А ТЕПЕРЬ О КОНКРЕТНЫХ ШАГАХ К НАШЕЙ ЦЕЛИ
- По возможности снижать размер и качество исходных файлов графики.
(1-a) Обрезка областей прозрачности.
В нашей игре использовалось много картинок с внушительными областями прозрачности вокруг изображения. Области прозрачности лишь на считанные проценты увеличивают размер файла и не очень влияют на размер сборки (хотя обрезка 102х немаленьких картинок сэкономила нам 2 Мбайт). Но области прозрачности увеличивают использование RAM (поскольку там картинки представляются в BMP).
Таким образом, совет: для снижения нагрузки на ОЗУ
* избегать больших областей прозрачности в изображениях;
* не формировать атласы с большими участками прозрачности, особенно в NGUI.
(Но это мы отвлеклись от основной темы.)
- Изображениям, не имеющим прозрачности и отображаемым в самом высоком качестве, в настройках импорта необходимо задать Advanced->RGB24. RGBA32 для них не имеет смысла, а вот оставлять Automatic Truecolor не рекомендуется, потому что (как показал опыт) он может восприняться системой как RGBA32. А это является совершенно лишним при отсутствии областей прозрачности (добавляет веса сборке и увеличивает использование RAM).
- Снизить качество изображений в настройках импорта по схеме: 32 bit -> 24 bit -> 16 bit, наблюдая за тем, чтобы уровень качества изображения оставался в пределах допустимого.
- Ограничить maxTextureSize для изображений, для которых это возможно с сохранением приемлемого качества.
- Тщательно вручную удалять неиспользуемые ассеты в папках Resources перед процессом сборки. Помните, что для ассетов, лежащих в папках Resources, Unity НЕ производит автоматическое их удаление, даже если они не назначены в Инспекторе.
- Для изображений JPG: сменить расширение файла на bytes и он превратится в TextAsset. После чего воспользоваться функцией Texture2D.LoadImage() для загрузки картинки. Следует помнить, что эта функция нагружает процессор и может оказаться неподходящей в случае больших картинок, которые необходимо очень быстро загрузить. Однако этот способ зачастую помогает внушительно облегчить сборку.
- Уменьшить размер звуковых файлов до минимально возможного размера (с учётом требований к качеству). По возможности использовать MP3, а не WAV. Но если нужны MP3 самого высокого качества, можно пользовать и WAV-файл с настройкой импорта AudioFormat = Compressed (Unity перекодирует самостоятельно). По возможности использовать Mono вместо Stereo. Для WAV-файлов — выставлять настройку импорта ForceToMono = true
После выполнения каждого пункта желательно проверять полученный эффект. Поскольку в поведении Unity всё же есть доля магии.
Несколько слов о dll
Документ приводит список обязательно включаемых в сборку dll и призывает минимизировать количество дополнительных dll (особенно тяжёлых). В частности, по возможности не использовать System.dll (добавляет к APK 2 Мбайт). Однако даже если мы будем избегать ссылок на методы из этой библиотеки, System.dll всё равно (по состоянию на Unity 4.5.5) включается в сборку, так как её подтягивает обязательная Mono.Security.dll. Потому, получается, что официальная документация Unity в этом месте не совсем релевантна.
А вот использования других (необязательных) dll желательно избегать во имя снижения размера сборки.
На сегодня у нас всё. Спасибо за внимание!
(Просьба рассматривать данный материал как более-менее систематизированные результаты исследования, а не как универсальное руководство к действию. Буду очень рад комментариям, замечаниям, конструктивной критике, возражениям, предложениям, дополнениям. И, конечно, несказанно рад буду узнать, какие приёмы применяют коллеги из других компаний для решения обсуждаемого вопроса.)