Оптимизация сборки — вишенка на торте мобильного приложения. К счастью, существуют инструменты, проверенные временем и заслужившие доверие сообщества. К сожалению, ее не всегда воспринимают всерьез и не уделяют ей должного внимания. Почему в оптимизации должны быть заинтересованы все? Как выжать максимум из мобильного приложения? Как работают инструменты, которыми мы привыкли пользоваться в паре строк? И что нам продают под словом «обфускация»?
О статье
Статья рассчитана на ознакомление с темой оптимизации сборок Android приложений. В статье:
Постановка проблемы. Обозначим общие требования к сборкам мобильных приложений;
Оптимизация сборки. Поговорим о том, как приложения собираются и как этот процесс можно оптимизировать;
ProGuard. Познакомимся с ProGuard и тем, как он оптимизирует сборки;
Тайны обфускации. Углубимся в проблему обфускации кода;
D8, R8. Узнаем, что такое D8, R8 и обозначим их место в процессе сборки.
После прочтения статьи у вас должно сложиться широкое понимание того, как работает оптимизация и какие инструменты для этого используются. Этих знаний будет достаточно для координации в предметной области и поиска решений конкретных задач, связанных со сборкой Android приложений.
Постановка проблемы
Сейчас почти каждый пользуется мобильными приложениями, а некоторые даже их разрабатывают. Так или иначе вы, как (пользователь / разработчик), стремитесь (иметь / создать) хороший продукт. Что значит “хороший продукт” в мире мобильных приложений? Хорошее мобильное приложение стремится:
быть размером 0 бит;
работать быстрее света;
быть безопаснее Зоны 51.
Чтобы понять, как этого добиться, необходимо понять, как работают мобильные приложения. Во-первых, это задача, которую мобильное приложение решает. Во-вторых, это сторонние сервисы, помогающие мобильному приложению решить поставленную задачу. В-третьих, это само мобильное приложение, решающее задачу при помощи сторонних сервисов.
От реализации каждой из этих компонент зависит степень соответствия установленным требованиям.
Оптимизация сборки
Мы выделили три компонента, необходимых для работы мобильного приложения:
задача, решаемая приложением (бизнес логика);
сторонние сервисы, помогающие в решении поставленной задачи;
мобильное приложение.
И три требования, удовлетворить которые мы стремимся:
минимизировать потребяемую память
повысить скорость работы
повысить безопасность
Сконцентрируемся на мобильных приложениях. Как правило, в команде разработчик выполняет одну роль, поэтому будем считать, что бизнес логика и сторонние сервисы прорабатываются не нами. Но не будем забывать, что мобильное приложение реализует в себе часть бизнес логики, взаимодействует со сторонними сервисами, и ответственность за это несет мобильный разработчик. Также не будем забывать, что от того, как реализованы эти компоненты (вне мобильного приложения), зависит качество вашего продукта, поэтому мотивируйте своих боевых товарищей (или самого себя, в случае full stack) делать качественно :)
Мобильная разработка. Приведу примеры способов оптимизации мобильных приложений согласно общим требованиям:
Минимизировать потребяемую память: использовать сборки, максимально соответствующие конфигурации устройства (split apks, app bundle); по возможности хранить данные на удаленном сервере; использовать Dynamic Delivery; удалять неиспользуемый код / ресурсы; и так далее.
Замечание
Учтите, что мобильное приложение использует память не только в процессе работы, но и по факту установки на устройство.
Повысить скорость работы: оптимизировать бизнес логику; правильно выбирать алгоритмы и структуры данных; делать сборки максимально соответствующие конфигурации устройства; мотивировать пользователя использовать производительные устройства оптимизировать код; и т.д…
Повысить безопасность: шифровать данные приложения; использовать безопасные каналы связи; ограничивать права; запутывать код; и т.д…
Мобильное приложение — специальным образом скомпилированный код, написанный разработчиком, установленный на устройство. Специальным образом скомпилированный код называют сборкой. От того, как производится сборка зависит:
сколько места займет приложение на устройстве;
как быстро оно будет работать;
насколько безопасным будет его использование.
Дальнейшее повествование будет вестись в разрезе Android разработки. Остальные — не переключайтесь.
Как производится сборка?
На схеме представлены этапы сборки Android приложения: от компиляции исходного кода до установки на устройство (Подробнее можно посмотреть тут). Каждый из этапов имеет значение для достижения исходной цели (память, скорость, безопасность). Но не в каждый из них можно внести качественные изменения: java, kotlin, DX, Android компиляторы, как правило, модифицируются их создателями (Oracle, JetBrains, Google).
Это не значит, что вы, как разработчик, никак не можете повлиять на вид итоговой сборки: есть возможность добавить промежуточные этапы согласно описанным контрактам между основными этапами. Пример такого контракта: DX компилятор принимает на вход Java байт-код. DX компилятору без разницы, как и откуда он получит этот байт-код, поэтому с ним можно сделать что угодно. Здесь подключаемся мы: модифицируем байт-код так, как нужно нам, и отдаем DX - вот и все!
Итого. Размер мобильного приложения, скорость его работы, безопасность его использования в конечном итоге зависят от того, какая сборка будет установлена на устройство. Все действия, призванные улучшить данные характеристики будем называть оптимизацией. Будем считать, что влиять на процесс сборки мы можем только посредством добавления промежуточных этапов, не нарушающих контракты между основными. К оптимизации!
ProGuard
Конечно, мы не первые, кто задается вопросами оптимизации Android приложений. В целом этот процесс состоит из этапов: сокращение кода, сокращение ресурсов, оптимизация кода, обфускация и не меняется от технологии к технологии. До недавних пор (см. раздел D8, R8), одним из самых популярных решений задачи оптимизации сборки Android приложений был — ProGuard (далее PG).
Определение
«ProGuard is a Java class file shrinker, optimizer, obfuscator, and preverifier.» (PG - это инструмент для сокращения, оптимизации (кода приложения), обфускации, верификации Java байт-кода). Такое определение своему продукту даёт Guardsquare. Кто такие GuardSquare?
О производителе
Guardsquare - компания, занимающаяся разработкой программ для защиты мобильных приложений. Среди них:
iXGuard - защиты iOS приложений и SDK
ProGuard - оптимизация Java и Android приложений
DexGuard - улучшенная версия ProGuard
ThreatCast - мониторинг безопасности iOS и Android приложений
Место ProGuard в процессе сборки приложения
Вспомните схему сборки Android приложения. Ниже представлен этап компиляции .class файлов в .dex.
PG встраивается перед этапом dex компиляции и поддерживает контракт: dex компилятор принимает на вход .class файлы, выдает .dex файлы. Данная “инъекция” не нарушает контрактов между основными этапами сборки: на выходе вы получите приложение, но с оптимизацией :)
Процесс оптимизации
Общее описание
Ниже представлена схема того, из каких этапов состоит PG оптимизация.
На вход PG подаются .jar файлы. Вспомним контракт c dex компилятором: на вход подается .class, но здесь .jar. На самом деле .jar — архив с .class файлами, метаинформацией, статическими ресурсами. Зачем PG понадобились статические ресурсы узнаем в разделе Shrink. Обратите внимание, что и на выходе также получаются .jar файлы. С этим нет проблем, так как это принятый в Java среде формат дистрибуции кода (приложений, библиотек), понятный PG и dex. В отличие от “Input jars”, которые изменяется в процессе, “Library jars” не изменяются PG и используются им в качестве контекста (Android Framework, Java SE, Java ME) для “Input jars” (Например, для Android Framework это android.jar, который находится на устройстве и недоступен в момент работы PG).
Все артефакты процесса оптимизации обозначены прямоугольниками, а сами оптимизации стрелками с соответствующими названиями. То есть на вход “shrink” подаются .jar файлы, они же (но уже измененные) подаются на вход “optimize” и так далее. Каждая из оптимизаций: “shrink”, “optimize”, “obfuscate”, “preverify” — опциональная. Например, вы можете отказаться от “obfuscate” или “shrink”, если это вам не нужно. Однако, порядок выполнение операций строго фиксирован. В текущей итерации PG вы, например, не можете выполнить “optimize” до “shrink”. Это правило существует для сокращения лишней работы. Смысл такой: оптимальнее сначала содрать обои, сделать уборку, а не сделать уборку, содрать обои (вы проникнитесь этой идеей при рассмотрении конкретных оптимизаций).
Shrink
Переводится как “сокращать”. Shrink сначала удаляет неиспользуемый приложением код затем ресурсы, тем самым сокращает итоговый вес .class файлов и статических ресурсов приложения. Такой порядок выполнения операций гарантирует, что ресурсы, к которым обращается неиспользуемый код (то есть используемые неиспользуемым кодом) будут удалены.
Code shrinking
Для того, чтобы понять, какой код следует удалить, нужно его классифицировать на достижимый и недостижимый. PG строит дерево достижимого кода, корнем которого является специальные входные точки, называемые seeds (от английского семена, источники).
На вход подаются seeds, которые по умолчанию считаются достижимыми и формируются в зависимости от среды исполнения кода (например, Activity в Android) и пользовательских правил.
Код каждого seed просматривается на предмет использования кода из других файлов. Весь используемый код помечается как достижимый и добавляется в очередь на проверку.
Для всех достижимых файлов из очереди выполняется тоже самое, что и для seed.
Весь достижимый код — сохраняется, весь недостижимый код — удаляется (tree shaking).
В чем особенность code shrinking в Android? Seeds формируются на основе:
Правил сгенерированных Android Gradle Plugin (далее AGP). Часть директив используемых по умолчанию находится в файлах: proguard-android.txt, proguard-android-optimize.txt.
Правил из подключенных библиотек, описанных в <library-dir>/proguard.txt (для .aar) или <library-dir>/META-INF/proguard/ (для .jar)
Пользовательских правил. Например, <module-dir>/proguard-rules.pro или <module-dir>/consumer-proguard-rules.pro
Resource shrinking
Принцип классификации ресурсов на достижимые и недостижимые схож с классификацией кода.
Сканируется весь достижимый код на факт использования ресурсов.
Используемые ресурсы помечаются как достижимые. Причем внутри одного файла могут быть как достижимые, так и недостижимые ресурсы.
Все достижимые ресурсы — сохраняются, все недостижимые ресурсы — удаляются (tree shaking).
Особенности resource shrinking в Android:
Ресурс считается достижимым, если используемые напрямую
val name = resources.getString(R.string.name))
или косвенно
val name = String.format("img_%1d", N + 1)
val res = resources.getIdentifier(name,"drawable",packageName)
Режимы работы resource shrinking:
Defense сохранит ресурсы используемые напрямую или косвенно
Strict сохранит ресурсы используемые напрямую
Поддерживает пользовательские правила. Например, в keep.xml.
Optimize
Данный этап нужно отличать от оптимизации сборки в целом. Optimize производит манипуляции с Java байт-кодом с целью его оптимизации с программной точки зрения.
Существуют различные техники оптимизации кода. Например, Control flow analysis — изменяет граф потока управления; Data-flow analysis — оптимизирует аллокаций и распространение данных; Partial evaluation — производит проактивное вычисление значений.
Пример оптимизации кода
Разберем пример того, как теоретически может работать оптимизация кода.
Предупреждение! Пример носит строго образовательный характер. В примере представлен исходный код, но на самом деле в PG изменяется байт-код. Приведенные в примере техники оптимизации могут отличаться в зависимости от выбранной технологии.
Посмотрите на исходное состояние кода.
Красным будут помечаться удаленные строки кода, зеленым — добавленные.
Перед тем, как читать пояснения, попробуйте самостоятельно оптимизировать данный код. Результат — в комментарии :)
“*.” и “*.example.com” — константы. Эти значения можно перенести в место их непосредственного использования и удалить их объявления в переменных. Более формально: произвести оптимизацию Data-Flow посредством Constant Propagation.
Обратите внимание, что “*.example.com” всегда начинается с “*.”. Результат данной операции может быть вычислен до запуска программы. Так техника оптимизации Partial Evaluation.
В данном условном выражении всегда используется одна ветвь, поэтому его можно “выпрямить”. Избавляемся от другой при помощи Control Flow оптимизации: Branch Prediction.
Еще один пример применения Partial Evaluation.
И еще один пример применения Partial Evaluation.
Итог. Посмотрите насколько упростился исходный код. Было 12 строк, стало 5 строк. Поразительный результат.
Особенности optimize в Android:
По умолчанию PG оптимизации не применяются для Android (proguard-android.txt:11 -donotoptimize инструкция)
Примеры оптимизаций поддерживаемых ProGuard:
class/marking/final (помечает классы как final, где это возможно)
class/unboxing/enum (заменяет enum на целочисленные константы, где это возможно)
class/merging/vertical (сливает классы по вертикали, устраняя ненужное наследование)
с остальными можно ознакомиться по ссылке.
Obfuscate
Переводится как “запутывать”. Часто его называют просто “обфускация”. Из руководства к ProGuard: “In the obfuscation step, ProGuard renames classes and class members that are not entry points. In this entire process, keeping the entry points ensures that they can still be accessed by their original names.” (На этапе обфускации PG переименовывает классы и его члены, которые не являются входными точками. Это сделано для того, чтобы входные точки были доступны извне по их исходным именам (Android OS должна знать реальное название компоненты, чтобы иметь возможность ее запустить))
Это необходимо для усложнения чтения вашего кода после декомпиляции вашего приложения злоумышленниками (reverse engineering).
Алгоритм обфускации кода
Обфускация производится за счет переименования названий, используемых в исходном коде (и несущих смысл), на неосмысленные.
Опишем общий алгоритм:
Все имена, используемые в приложении: имена пакетов, классов, методов, полей, выносятся в отдельный словарь (словарь обфускации).
Всем именам из словаря даются новые (обфусцированные) имена.
На основе составленного словаря код приложения переименовывается (обфусцируется).
Почему используется название “short name” читайте в главе «Тайны обфускации кода».
Пример обфускации кода
Разберем пример.
Предупреждение! Пример носит строго образовательный характер. В примере представлен исходный код, но на самом деле в PG изменяется байт-код. Приведенные в примере техники оптимизации могут отличаться в зависимости от выбранной технологии.
На вход подается необфусцированный код. Он сканируется на наличие имен.
Строится словарь обфускации: всем исходным именам приводятся в соответствие новые — обфусцированные.
По словарю обфускации переименовывается код, поданный на вход. Получаем результат.
Этап обфускации подробно рассмотрен в разделе «Тайны обфускации кода».
Preverify
Переводится как “предварительно проверить”. На этом этапе осуществляется проверка кода и ресурсов на соответствие требованиям среды эксплуатации.
Особенности preverify в Android:
«Preverification is irrelevant for the dex compiler and the Dalvik VM, so we can switch it off with the -dontpreverify option.» (Предварительная верификация неуместна при использования dex компилятора и Dalvik VM, поэтому мы может выключить этот этап используя флаг -donotpreverify.
Флаг -android укажет на необходимость произвести проверку на соответствие target API и совместимости.
Тайны обфускации кода
Мы разобрали как ProGuard реализует этап obfuscate. Но можно ли на этом заканчивать? Давайте разбираться.
Фактическая реализация
На сайте developer.android.com можно найти текст “The purpose of obfuscation is to reduce your app size by shortening the names of your app’s classes, methods, and fields” (Цель обфускации - сократить размер приложения за счет сокращения имена классов, метод, полей приложения).
Вспоминаем пример
До обфускации:
После обфускации:
Сравним количество символов кода до и после обфускации. Получаем 402/250 — чуть больше чем в 1.5 раза сократился текст исходного кода. Цель обфускации, согласно определению гугла достигнута. Но почему в тексте нет ничего про запутывание?
Реальный смысл
Знатоки латыни скажут: «obfuscare — затенять, затемнять»; знатоки английского скажут «obfuscate — делать неочевидным, запутанным, сбивать с толку». Не совсем одно и тоже, что и сокращение имён, не правда ли?
Определение
Согласно википедии “…obfuscate code to conceal its purpose (security through obscurity) or its logic or implicit values embedded in it, primarily, in order to prevent tampering, deter reverse engineering…” (… обфусцировать код с целью скрытия его исходного смысла (защита через запутывание) или его логики или литералов записанных в коде, преимущественно, чтобы предотвратить внедрение в код, усложнить обратный инжиниринг …).
Еще раз
GuardSquare, Google: обфускация - сокращение имён.
Wikipedia: обфускация - запутывание смысла кода.
Критика
Обфускация в ProGuard и в R8 (о котором поговорим далее) — минификация кода посредством замены имён более короткими и менее осмысленными. Такой подход лишь ЧАСТИЧНО соответствует исходному смыслу обфускации: скрытия исходного смысла, логики, литералов.
Вспомните строчку “minifyEnabled = true” из build.gradle. MINIFY. OBFUSCATE. Поздравляю, мы купили второй пылесос.
Отрицание
Кто-то возразит:
«Посмотрите на стектрейс обфусцированного кода. Почти ничего не понятно!», И будет прав. Действительно, ПОЧТИ ничего непонятно, но дело в том, что иногда этого ПОЧТИ недостаточно.
Гнев
Другой сделает reverse engineering вашего приложения и приведет пример со словами: “литералы и бизнес логика прослеживаются!”. И будет прав. Действительно, при стандартной настройке обфускации литералы и бизнес логика нетрудно прослеживаются.
Торг
Давайте попробуем разобраться, почему нам не стоит беспокоиться об обфускации через минификацию (ProGuard, R8 подход)? Вот некоторые аргументы:
Часть злоумышленников отпадет (со смеху :)) при виде минифицированного байт-кода;
Приложение не содержит ценную бизнес логику, то есть нам нечего скрывать.
Часть людей успокоилась. Я рад. Но не торопитесь уходить.
Депрессия
Давайте зададим пессимистичный вопрос: “почему нам стоит беспокоиться?”. Предположительные ответы:
Минификация не запутывает байт-код
Ценная бизнес логика прослеживается
Видны важные литералы
…
Для более “толстых” и потенциально опасных приложений минификация исходного кода не выдерживает критики. Но обфускация не заканчивается минификацией и тут появляется надежда…
Надежда
До этого момента для нас обфускация начиналась и заканчивалась одной лишь минификацией (переименованием). Благо, это далеко не так. Минификация лишь один из множества техник обфускации. Приведу примеры других возможных приемов обфускации: запутывание названий; шифрование литералов; обфускация control flow; преобразования языковых конструкций; замена высокоуровневых инструкций низкоуровневыми эквивалентами; вставка фиктивного кода; удаление метаданных; сокращение информацию, которой может воспользоваться взломщик; слияние кода; запутывание структуры исходного кода (файлы, папки); anti-tampering; сброс и логирование попыток вмешательства в работу приложения; и другие…
ProGuard
(Достает из грязи) несмотря на то, что PG не поддерживает все техники обфускации, он способен делать больше, чем просто минифицировать код. Для более тонкой настройки можно воспользоваться опциями:
-flattenpackagehierarchy […] - складывает весь код в один пакет
-dontusemixedcaseclassnames - имена в нижний регистр
-adaptclassstrings […] - обфускация имён строковых констант
…
Стоит вспомнить, что PG лишь один из продуктов GuardSquare. Он бесплатен, но существует и коммерческий продукт — DexGuard, позиционируемый как усовершенствованная версия PG. Вот что пишет о нем GuardSquare:
DexGuard полность совместимость с ProGuard
Помимо обфускации имён, DexGuard способен обфусцировать арифметические выражения, control flow, нативный код, имена библиотек, ресурсов, вызовов SDK, шифровать классы, ресурсы, нативные библиотеки…
Звучит неплохо. Проверить его в деле доверяю дорогим читателям (обязательно напишите свою оценку этого продукта, если имеете опыт использования).
Open source
Как хорошо, что есть ProGuard и DexGuard, но на них история не заканчивается. Существует ряд open source обфускаторов. Среди них:
Все хотят узнать ваш опыт использования этих технологий. Прошу в комментарии.
Не стоит забывать, что Android приложения пишутся не только на Java и Kotlin. На случай, если вы используете C/C++: obfuscator, movfuscator; JavaScript: javascript-obfuscator.
Опять же, ваш опыт — в комментарии.
D8, R8
При изучении вопроса оптимизации сборок Android приложений можно наткнуться на эти две аббревиатуры. Разберёмся, что они значат и какое отношение имеют к оптимизации сборок, PG и Android в целом.
Небольшой спойлер. До погружения в вопрос оптимизации сборок, я думал, что использую ProGuard.
Вспомним:
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), proguard-rules.pro'
proguard-rules.pro, consumer-rules.pro
ProGuard, ProGuard, ProGuard… Оказывается, что фактически я использовал R8 (начиная с AGP версии 3.4). Также я думал, что использую DX, но фактически использовал D8 (начиная с AGP версии 3.2).
Почему так вышло? Читайте далее.
D8
Определение
Что такое D8? Google (его создатели) позиционирует D8 как dex компилятор нового поколения.
К слову. Он используется по умолчанию с версии 3.2 AGP. Именно поэтому я не заметил его появление в своей жизни: всего лишь обновил AGP, а тут вот что. Урок — читайте release notes продуктов, которые используете.
Мотивация
Зачем он нужен? Вспомним схему встраивания PG при сборке приложения. .dex файл компилируется DX компилятором.
Так выглядел процесс компиляции .dex до появления D8. Обратите внимание на количество шагов: 2 - для Kotlin кода, 3 - для Java.
Что стало после появления D8:
D8 вобрал себя этап desugar, тем самым сократил общее число шагов компиляции: 2 - для Kotlin кода, 2 - для Java.. К чему это привело?
D8 vs DX
Google приводить сравнительный анализ компиляторов DX (используемый по умолчанию до появления D8) и нового D8. Обратите внимание:
D8 ощутимо быстрее компилирует исходный код;
Размер выходного .dex немного меньше при использовании D8.
R8
Определение
R8, как и D8, разрабатывается Google и также связан со сборкой Android приложений. Но R8 — не .dex компилятор, он — оптимизатор.
R8 используется по умолчанию с версии 3.4 AGP вместо PG. Тут я опять удивился положению вещей. Особенно потому, что раньше я писал правила для ProGuard, а понимает их R8. 10 очков Google за хорошую совместимость c PG правилами. Повторяю урок — читайте release notes продуктов, которые используете.
Мотивация
Посмотрим, чем мотивировал себя Google при разработке этого оптимизатора. Так выглядел процесс сборки до появления R8.
Так он стал выглядеть после:
История схожа с D8: оптимизировать процесс сборки за счет сокращения промежуточных этапов. Но! Вспомните еще один момент, чей ProGuard? — GuardSquare, а чей R8? — Google. Отсюда можно сделать еще один вывод о мотивации Google к созданию R8 — замена сторонней технологии свой собственной. И тут родился конфликт…
R8 vs ProGuard
Появилось две статьи (сначала от Google, потом о GuardSquare) со сравнительным анализом R8 и ProGuard.
Вкратце, о чем пишет Google:
R8 (в комбинации с D8) в compact и full режимах почти в 2 раза быстрее “шринкует” и компилирует .dex.
Размер выходного .dex немного меньше, чем у PG.
Не нужно забывать, что Google, как владелец Android, имеет бОльшее влияние на развитие платформы и сопутствующих технологий. Именно поэтому они могут позволить себе заменить DX компилятор на D8 (один из основных этапов (см. раздел Оптимизация сборки) и встроить сверху R8.
О чем пишет GuardSquare. Основной аргумент GS в пользу PG заключается в большем количестве оптимизаций кода: 520 — ProGuard к 6 — R8 (на момент написания статьи — 23 октября 2019). Сравнительная таблица видов оптимизаций из статьи доступна по ссылке. Некоторые из них:
Также GuardSquare утверждает, что ProGuard работает быстрее при малом количестве итераций, но тут стоит сделать одно замечание: чем меньше итераций, тем меньше оптимизаций, как следствие бОльший размер .apk, менее оптимизированный код.
Стоит отдать должное GuardSquare за то, что их продукт разрабатывается более 15 лет, в то время как R8 стал доступен в лишь 2018.
Резюме
Было интересно окунуться в необычные детали такой, казалось бы, обычной задачи как оптимизация сборки. Надеюсь, вы разделили радость познания вместе со мной.
Подведем итоги:
Задача, сторонние сервисы, мобильное приложение — вот компоненты, от которых зависит итоговый размер приложения, скорость его работы и безопасность его использования. Требования, предъявляемые к каждой из этих компонент, определяют итоговое решение.
Процесс сборки Android приложений состоит из основных этапов: java / kotlin компиляция, dex компиляция. Между основными этапами определены строгие контракты, поддерживая которые можно встроить дополнительные этапы. Например, оптимизация кода, ресурсов.
Оптимизация сборки в общем случае состоит из этапов: сокращение кода, ресурсов (память); оптимизация кода (скорость); обфускация (безопасность).
Обфускация — приведение кода к виду, сохраняющему его функциональность, но затрудняющему анализ. Запутывание имен — одна из многих техник обфускации, часто используемая в популярных оптимизаторах.
ProGuard — java байт-код оптимизатор, способный оптимизировать сборки Android приложений. Разработан GuardSquare.
D8, R8 — технологии, разработанные Google, призванные улучшить процесс сборки (D8) и ее оптимизации (R8). R8 — позиционируется как замена ProGuard. Что использовать в итоге — решать вам.
Ссылки
Ссылки на источники приложены в месте их непосредственного использования.
Полезные материалы:
Android Developers. Shrink, obfuscate, and optimize your app.
Android Developers, Medium. Practical ProGuard rules examples.
Android Developers, Medium. Troubleshooting ProGuard issues on Android
IMStudio, Medium. Android Journey: Proguard, D8, R8 what are they?
inwady, Habr. 6 способов спрятать данные в Android-приложении.
forceLain, Habr. Как перестать бояться Proguard и начать жить.
Автор статьи Валерий Петров (@valeryvpetrov)
kosikov2006
Я точно не поставлю 10 баллов за совместимость R8 c PG правилами. Потому что переход с PG на R8 был для меня болезненным. У меня было среднее по размеру приложение (где-то на 15 экранов), PG работал на настройках по-умолчанию, все работало. А когда я обновил AGP, в котором заменили PG на R8, у меня приложение стало падать. Причина оказалась как раз в том, что в R8 работает не точно также как PG. R8 почему-то удалил некоторые вещи в моём коде, и пришлось явно прописать удаленные вещи в правилах R8, чтобы они не удалялись. При следующем обновлении AGP, R8 стал удалять ещё что-то, я тогда плюнул на все это, и просто прописал в настройках следующее правило:
# Не удалять, но обфусцировать классы и их содержимое
-keep,allowobfuscation class ** { *; }
valeryvpetrov
kosikov2006. Согласен, это два не совершенно одинаковых продукта. Совместимость PG-R8 распространяется на уровне поддержки файлов с правилами, но не абсолютно всех правил и уж тем более их реализации (вспомните хотя бы сравнение оптимизаций (520 к 6)).
android.enableR8=false
android.enableR8.libraries=false
Уверен, у вас всё получится :)