- «MinifyEnabled» против «shrinkResources» – какая разница? И как получить сохраненное пространство?
- Задний план
- Вопросы
- Играем в APK-гольф. Уменьшение размера файлов Android APK на 99,9%
- Базовый уровень
- APK Analyser
- Ресурсы
- Подпись
- AndroidManifest
- Включаем минификацию
- 786 КБ (уменьшение на 50%)
- Прощай, AppCompat, мы едва тебя узнали
- 108 КБ (уменьшение на 87%)
- 6808 байт (уменьшение на 94%)
- Файл шаблона (6262 байта, сокращение на 9%)
- Имя приложения (6034 байта, сокращение на 4%)
- Иконка лаунчера (5300 байт, сокращение на 13%)
- Манифест (5252 байта, сокращение на 1%)
- Хак Proguard (4984 байта, сокращение на 5%)
- Обфускация (4936 байт, сокращение на 1%)
- META-INF (3307 байт, сокращение на 33%)
- Куда мы идём — там не нужны IDE
- Несоответствие размеров файлов (2608 байт, сжатие на 21%)
- Хаки со сжатием (2599 байт, сокращение на 0,5%)
- Привет, ADB (2462 байт, сокращение на 5%)
- Очистка от ссылок на методы (2179 байт, сокращение на 12%)
- Оптимизация Dex (1961 байт, сокращение на 10%)
- Понимание манифеста (1961 байт, сокращение на 0%)
- Непонимание манифеста (1777 байт, сокращение на 9%)
- Манифест UTF-8
- Шестнадцатиричный манифест
- Готово? (1757 байт, сокращение 1%)
- Шаг 5: Признание
«MinifyEnabled» против «shrinkResources» – какая разница? И как получить сохраненное пространство?
Задний план
Согласно веб-странице «Ресурс сокращения» в документах Andriod ( здесь ), вы можете минимизировать размер приложения через файл build.gradle, используя следующие строки:
И, они говорят, что при его использовании он также расскажет вам, сколько из них сохраняется в процессе:
Когда вы включаете shrinkResources, для создания вашего приложения должен отображаться следующий вывод во время сборки:
… Удаленные неиспользуемые ресурсы: данные двоичных ресурсов уменьшены с 2570 КБ до 1711 КБ: Удалены 33%
Вопросы
Я не могу найти ответы на эти вопросы:
- При использовании Android-Studio для создания подписанного приложения, где я могу найти информацию о том, сколько было сохранено и какие файлы были удалены / изменены?
- Что именно означает «shrinkResources», что «minifyEnabled» нет? И почему «shrinkResources» зависит от «minifyEnabled»?
- Может ли какой-либо из этих параметров влиять на размер и / или качество файлов изображений?
- Разве Proguard не отвечает за сокращение исходного кода? Я спрашиваю об этом, потому что он говорит: «вы должны включить minifyEnabled, чтобы включить сокращение кода»,
При использовании Android-Studio для создания подписанного приложения, где я могу найти информацию о том, сколько было сохранено и какие файлы были удалены / изменены?
Они будут в журнале градации. Внутри студии Android я считаю, что они отображаются в окне « Messages (рядом с окнами Android, Run, TODO).
Что именно означает «shrinkResources», что «minifyEnabled» нет? И почему «shrinkResources» зависит от «minifyEnabled»?
minify запускает ProGuard. shrink удаляет ресурсы, которые ProGuard отмечены как неиспользуемые.
Может ли какой-либо из этих параметров влиять на размер и / или качество файлов изображений?
Разве Proguard не отвечает за сокращение исходного кода? Я спрашиваю об этом, потому что он говорит: «вы должны включить minifyEnabled, чтобы включить сокращение кода»,
ProGuard сжимает только CODE ; shrinkResources это просто материал из папки /res/ . shrinkResources зависит от выхода журнала из ProGuard для запуска. ProGuard – это тот, кто фактически анализирует код, чтобы знать, что не используется.
редактировать:
Я только что нашел очень хороший пост в блоге. CommonsWare отправил его на другой вопрос stackOverlow: http://cyrilmottier.com/2014/08/26/putting-your-apks-on-diet/
Это прекрасно объясняет ваш следующий вопрос:
Почему одно зависит от другого?
Proguard работает на стороне Java. К сожалению, он не работает на стороне ресурсов. Как следствие, если образ my_image в res / drawable не используется, Proguard только удаляет его ссылку в классе R, но сохраняет связанное изображение на месте.
Это означает, что shrinkResources сравнивается только в том случае, если drawable находится в папке, но не в классе R
Ответы на вопросы 2 и 4 можно найти в этом видео с Android Dev Summit 2015 вместе с другой полезной информацией по этой теме.
Обзор рассмотренных вопросов:
shrinkResources учитывается, только если minifyEnabled истинно
minifyEnabled сокращает код, а shrinkResources сокращает ресурсы, на которые не ссылаются код
По умолчанию shrinkResources работает в safe режиме. Если вы переключите его на strict вы можете предоставить tools:keep и tools:discard флаги вручную, чтобы повлиять на сокращение ресурсов.
Источник
Играем в APK-гольф. Уменьшение размера файлов Android APK на 99,9%
В гольфе выигрывает тот, у кого меньше очков.
Применим этот принцип в Android. Мы собираемся поиграть в APK-гольф и создать приложение минимально возможного размера, которое можно установить на Android 8.0 Oreo.
Базовый уровень
Начнём с дефолтного приложения, который генерирует Android Studio. Создадим хранилище ключей, подпишем приложение и измерим размер файла в байтах командой stat -f%z $filename .
Затем установим APK на смартфон Nexus 5x под Oreo, чтобы убедиться, что всё работает.
Прекрасно. Наш APK весит примерно полтора мегабайта.
APK Analyser
Полтора мегабайта кажутся слишком большим размером с учётом того, что делает наше приложение (а оно ничего не делает), так что давайте изучим проект и поищем, где по-быстрому сэкономить на объёме. Вот что сгенерировал Android Studio:
- MainActivity , который расширяет AppCompatActivity .
- Файл макета с ConstraintLayout для главного окна.
- Файлы ресурсов с тремя цветами, одним строковым ресурсом и темой.
- Библиотеки поддержки AppCompat и ConstraintLayout .
- Один AndroidManifest.xml .
- Файлы PNG для квадратной, круглой и фоновой иконок.
Пожалуй, проще всего разобраться с иконками, учитывая, что там в общей сложности 15 изображений и два XML-файла под mipmap-anydpi-v26 . Давайте посчитаем всё это в APK Analyser из Android Studio.
Вопреки нашим первоначальным предположениям, похоже, что самый большой файл — Dex, а на ресурсы приходится всего 20% от размера APK.
Файл | Размер | ||||||
---|---|---|---|---|---|---|---|
classes.dex | 74% | ||||||
res | 20% | ||||||
resources.arsc | 4% | ||||||
META-INF | 2% | ||||||
AndroidManifest.xml | classes.dex — главный виновник раздутого APK, он занимает 73% всего объёма и поэтому станет первой целью оптимизации. Этот файл содержит весь наш скомпилированный код в формате Dex, а также список внешних методов во фреймворке Android и библиотеку поддержки. В пакете android.support перечисляется более 13 000 методов, что кажется излишним для приложения типа «Hello World». РесурсыВ директории res находится большое количество файлов шаблонов, чертежей (drawables) и анимаций, которые сразу не видны в интерфейсе Android Studio. Опять же, они вытянуты из библиотеки поддержки и занимают около 20% размера APK. Файл resources.arsc также содержит список всех этих ресурсов. ПодписьВ папке META-INF находятся файлы CERT.SF , MANIFEST.MF и CERT.RSA , которые нужны для подписи v1 APK. Если злоумышленник изменит код внутри APK, то подписи не совпадут, что защищает пользователя от запуска постороннего зловреда. В MANIFEST.MF перечисляются файлы из APK, а CERT.SF содержит контрольные суммы манифеста и каждого отдельного файла. В CERT.RSA хранится открытый ключ, которым проверяется цельность CERT.SF . Здесь нет очевидных целей для оптимизации. AndroidManifestAndroidManifest очень похож на наш оригинальный файл. Единственное отличие — вместо ресурсов вроде строк и drawables здесь указаны их целочисленные идентификаторы, начиная с 0x7F . Включаем минификациюМы ещё не пробовали включить опцию минификации и сжатия ресурсов в файле build.gradle для нашего приложения. Сделаем это. Если установить minifyEnabled в значение true , то активируется Proguard, который очищает приложение от ненужного кода. А также обфусцирует имена символов, затрудняя обратную разработку приложения. shrinkResources удалит из APK любые ресурсы, на которые нет прямой ссылки. Могут возникнуть проблемы, если вы получаете доступ к ресурсам не напрямую, но к нашему приложению это не относится. 786 КБ (уменьшение на 50%)Мы наполовину уменьшили размер APK без видимого изменения в работе программы. Если вы ещё не включили minifyEnabled и shrinkResources в своём приложении, это самая главная вещь, которую следует вынести из этой статьи. Можно легко сэкономить несколько мегабайт, потратив всего парочку часов на конфигурацию и тестирование. Прощай, AppCompat, мы едва тебя узналиclasses.dex теперь занимает 57% всего APK. Основная часть списка методов из файла Dex принадлежит пакету android.support , так что мы собираемся удалить библиотеку поддержки. Для этого нужно сделать следующее:
Обновить MainActivity для расширения класса android.app.Activity . Обновить наш шаблон для использования единого TextView . 108 КБ (уменьшение на 87%)Матерь божья, файл уменьшился почти в десять раз: с 786 КБ до 108 КБ. Единственным заметным изменением стало только изменение цвета тулбара, который окрасился в дефолтную тему ОС. На директорию res теперь приходится 95% размера APK из-за всех этих иконок лаунчера. Если бы эти иконки делал наш дизайнер, мы бы попытались конвертировать их в WebP, более эффективный формат, который поддерживается в API 15 и более поздних версиях. К счастью, Google уже оптимизировала наши drawables, хотя в противном случае мы бы и сами могли оптимизировать их и удалить из PNG ненужные метаданные с помощью ImageOptim. Давайте поступим нешаблонно — и заменим все наши иконки запуска единственной однопиксельной чёрной точкой в папке res/drawable . Эта картинка весит 67 байт. 6808 байт (уменьшение на 94%)Мы избавились почти от всех ресурсов, так что неудивительно, что размер APK уменьшился примерно на 95%. В файле resources.arsc по-прежнему упоминаются следующие ресурсы:
Пойдём сверху вниз. Файл шаблона (6262 байта, сокращение на 9%)Фреймворк Android раздувает наш файл XML и автоматически создаёт объект TextView , который используется как contentView для Activity . Попробуем обойтись без этого посредника, удалив файл XML и программно задав contentView. Объём ресурсов уменьшится, потому что исчезнет файл XML, но увеличится размер файла Dex, поскольку мы упоминаем там дополнительные методы TextView . Выглядит как неплохой обмен. Имя приложения (6034 байта, сокращение на 4%)Давайте удалим strings.xml и заменим android:label в манифесте AndroidManifest буквой «A». Это кажется маленьким изменением, но удаление записи из resources.arsc уменьшает количество символов в манифесте и удаляет файл из директории res. Каждая мелочь идёт на пользу — мы только что сэкономили 228 байт. Иконка лаунчера (5300 байт, сокращение на 13%)Документация для resources.arsc в репозитории Android Platform объясняет, что каждый ресурс APK упоминается в resources.arsc с целочисленным идентификатором. У этих ID два пространства имён:
0x7f: ресурсы приложения (в файле .apk приложения) Так что произойдёт с нашим APK, если мы поставил ссылку на ресурс в пространстве имён 0x01? По идее, мы получим более красивую иконку и одновременно уменьшим размер своего файла. Само собой, вам никогда не следует доверять системным ресурсам вроде иконок в реальном рабочем приложении. Такой метод провалит валидацию в Google Play, а некоторые производители ещё и по-своему определяют белый цвет, так что действуйте осторожно. Манифест (5252 байта, сокращение на 1%)Мы ещё не трогали манифест. Удаление этих аттрибутов экономит 48 байт. Хак Proguard (4984 байта, сокращение на 5%)Похоже, что классы BuildConfig и R ещё остались в файле Dex. Уточнение правила Proguard удалит ненужные классы. Обфускация (4936 байт, сокращение на 1%)Обфусцируем имя для класса Activity. Для обычных классов Proguard автоматически делает это, но поскольку имя класса Activity вызывается через Intents, его не обфусцировали по умолчанию. META-INF (3307 байт, сокращение на 33%)В данный момент мы подписываем приложение одновременно подписями v1 и v2. Это кажется лишней тратой ресурсов, потому что v2 обеспечивает превосходную защиту и производительность, хешируя весь APK целиком. Подпись v2 не видна из APK Analyser, поскольку включена в бинарный блок в самом файле APK. Подпись v1 видна, в виде файлов CERT.RSA и CERT.SF . Давайте уберём галочку для подписи v1 в интерфейсе Android Studio и сгенерируем подписанный APK. Попробуем сделать и наоборот.
Похоже, теперь мы будем использовать v2. Куда мы идём — там не нужны IDEПришло время редактировать APK вручную. Используем следующие команды: Детальный обзор процесса подписи APK см. здесь. В общем, Gradle генерирует неподписанный архив, zipalign делает выравнивание по границе байта для несжатых ресурсов, чтобы оптимизировать потребление RAM после загрузки APK, и в конце запускается криптографическая процедура подписи APK. Неподписанный и невыровненный APK весит 1902 байт, то есть процедура добавляет примерно 1 килобайт. Несоответствие размеров файлов (2608 байт, сжатие на 21%)Странно! Если разархивировать невыровненный APK и подписать его вручную, то пропадает файл META-INF/MANIFEST.MF , что экономит 543 байта. Если кто-то знает, почему так происходит, то дайте знать! Теперь у нас в подписанном APK осталось три файла. Но ведь мы можем ещё избавиться от файла resources.arsc , потому что не устанавливаем никаких ресурсов! После этого остаётся только манифест и файл classes.dex , оба примерно одинакового размера. Хаки со сжатием (2599 байт, сокращение на 0,5%)Теперь изменим все оставшиеся строки на ‘c’, обновив версии до 26, а затем сгенерируем подписанный APK. Это уменьшает размер ещё на 9 байт. Хотя количество символов в файле не изменилось, но дело в том, что увеличилась частотность символа ‘c’. В результате алгоритм сжатия сработал более эффективно. Привет, ADB (2462 байт, сокращение на 5%)Можно ещё сильнее оптимизировать манифест, удалив фильтр намерения Launch для класса Activity. С этого момента будем запускать приложение следующей командой: adb shell am start -a android.intent.action.MAIN -n c.c/.c Вот новый манифест: Мы также избавились от иконки лаунчера. Очистка от ссылок на методы (2179 байт, сокращение на 12%)По изначальным условиям, мы должны подготовить APK, который способен установиться на устройство. Наше приложение перечисляет методы в классах TextView , Bundle и Activity . Можно уменьшить размер файла Dex, удалив эти ссылки и заменив их новым классом Application . Таким образом, файл Dex теперь будет ссылаться на единственный метод — конструктор класса Application . Исходные файлы теперь выглядят следующим образом: Используем adb для проверки, что APK успешно установился, это можно также проверить через «Настройки». Оптимизация Dex (1961 байт, сокращение на 10%)Я потратил несколько часов, изучая формат файла Dex ради этой оптимизации, поскольку разные механизмы вроде контрольных сумм и смещений затрудняют ручное редактирование. Если вкратце, в итоге выяснилось, что единственным требованием для установки APK является факт существования файла classes.dex . Поэтому мы просто удалим оригинальный файл, запустим touch classes.dex в консоли и сэкономим 10% от размера, используя пустой файл. Иногда глупейшее решение — самое лучшее. Понимание манифеста (1961 байт, сокращение на 0%)Манифест неподписанного APK — это файл в бинарном формате XML, который вроде бы официально не документирован. Можно изменить содержимое файла с помощью редактора HexFiend. В заголовке файла угадываются некоторые интересные элементы — первые четыре байта кодируют 38 , что совпадает с номером версии файла Dex. Следующие два байта кодируют 660 , что совпадает с размером файла. Попробуем удалить один байт, установив targetSdkVersion на 1 , и изменив размер файла в заголовке на 659 . К сожалению, система Android отвергает новый файл как неправильный APK. Похоже, тут всё устроено как-то посложнее… Непонимание манифеста (1777 байт, сокращение на 9%)А попробуем набросать случайных символов по всему файлу, а затем установить APK, не изменяя указанный размер файла. Так мы проверим, осуществляется ли проверка контрольной суммы, и как наши изменения повлияют на смещения в заголовке файла. Удивительно, но такой манифест воспринят как валидный APK на Nexus 5X под Oreo: Мне кажется, я только что услышал, как разработчик фреймворка Android, ответственный за поддержку BinaryXMLParser.java , очень громко закричал в подушку. Для максимальной выгоды нужно заменить все эти глупые символы нулевыми байтами. Это поможет распознать важные части файла в HexFiend, а также сократит несколько байт благодаря хаку сжатия, упомянутому выше. Манифест UTF-8Вот важные компоненты Manifest, без которых APK не установится. Некоторые вещи очевидны, такие как теги манифеста и пакета. В пуле строк видны versionCode и название пакета. Шестнадцатиричный манифестПросмотр файла в шестнадцатиричном виде показывает значения в заголовке файла, которые описывают пул строк и другие значения, вроде размера файла 0x9402 . Строки тоже интересно закодированы — если они больше 8 байт, то общая длина указывается в двух предыдущих байтах. Но вряд ли здесь можно найти другие варианты для оптимизации. Готово? (1757 байт, сокращение 1%)Изучим окончательный APK. В течение всего этого имени в APK было указано моё имя в подписи v2. Создадим новое хранилище ключей, в котором используется хак для сжатия. Мы сэкономили 20 байт. Шаг 5: Признание1757 байт — это очень мало, чёрт возьми. И насколько я знаю, это самый маленький существующий APK. Однако я разумно полагаю, что кто-нибудь из Android-сообщества способен выполнить дальнейшие оптимизации и ещё улучшить результат. Если вы умудритесь уменьшить файл с нынешних 1757 байт, присылайте пулл-реквест в репозиторий, где хостится самый маленький APK, или сообщайте в твиттере. (С момента публикации статьи файл уже уменьшили до 820 байт — прим. пер.) Источник |