Истории успеха

Как компания Grab снизила количество ошибок ANR на 32% и ускорила запуск приложения на 35% с помощью R8 и оптимизации на основе профиля

Время на чтение: 5 минут
3 Авторы
Ajesh Pai, Nguyen Van Minh, Tracy Agyemang

Grab – это ведущее суперприложение в Юго-Восточной Азии, которое предлагает услуги доставки, мобильности и финансов миллионам пользователей в восьми странах. База кода Grab существует уже 10 лет и содержит более 10 миллионов строк кода и сотни сторонних библиотек, созданных с помощью гибридной настройки Bazel и Gradle. Команда Grab столкнулась с тремя растущими проблемами: высокая доля ошибок ANR, размер приложения, который продолжал расти по мере добавления новых сервисов, и время запуска, которое медленно увеличивалось из года в год. Включив оптимизацию R8, уточнив правила сохранения и внедрив профили базовой конфигурации и запуска, разработчики Grab снизили долю ошибок ANR в активном режиме на 32%, уменьшили размер скачиваемого приложения примерно на 30 % и сократили время запуска на 35%.

Как устранить всплески ошибок ANR после выпуска и улучшить пользовательский опыт

До оптимизации процесса сборки в Grab сталкивались с задержками при холодном запуске и резким ростом числа ошибок ANR сразу после выхода обновлений. В основном это было связано с тем, что при инициализации приложения считывалось слишком много DEX-файлов.

Облачные профили Google Play оптимизируют стандартные процессы для всей пользовательской базы, но не все пути инициализации, необходимые для критических пользовательских сценариев Grab, например инициализацию графов зависимостей Dagger при запуске приложения. Для путей выполнения, не охваченных первоначальным распределением, устройства полагались на JIT-компиляцию и локальную фоновую оптимизацию в последующие дни. Чтобы решить эту проблему, разработчики внедрили собственный базовый профиль. Это позволило добавить алгоритмы достижения цели в облачные профили Google Play и расширить область оптимизации. В результате ключевые пути стали компилироваться в машинный код сразу после установки.

ANDDM_Grab_Inline_Quote_ALT.png

"Мы постоянно сталкивались со всплесками доли ошибок ANR при каждом еженедельном выпуске приложения, – объясняет Нгуен Ван Минь (Nguyen Van Minh), ведущий инженер-программист в команде Passenger App Core в Grab. –

Чтобы устранить ошибки ANR и ускорить запуск, разработчики Grab внедрили оптимизацию R8, а также профили базовой конфигурации и запуска. Оптимизация на основе профиля и оптимизатор R8 сразу же улучшили холодный запуск на 12% и 15% соответственно. В результате время запуска сократилось на 27 %, а количество ошибок ANR после выпуска снизилось.

Оптимизация приложения Grab проходила в три этапа: включение оптимизации R8 в режиме совместимости, переход на полный режим R8, а затем уточнение правил сохранения с помощью анализатора конфигурации и добавление управления CI/CD.

Этап 1. Включение оптимизации R8  (выпущено в 2025 году)

На первом этапе разработчики Grab отказались от устаревшего файла Proguard по умолчанию и перешли на proguard-android-optimize.txt, который позволяет оптимизировать R8. Поскольку база кода была создана более десяти лет назад и содержит более 10 миллионов строк кода, для включения оптимизатора потребовалось устранить устаревшие конфигурации и использование рефлексии.

Чтобы добиться максимального эффекта, компания выпустила обновление приложения с включенным режимом совместимости R8, а также профилями базовой конфигурации и запуска. В результате доля ошибок ANR в активном режиме сразу снизилась на 25 %, размер приложения при скачивании уменьшился на 16 %, а время холодного запуска сократилось на 27 %.

Этап 2. Полный режим R8 и перенос (выпуск в 2026 г.)

На этом этапе, который начался через девять месяцев после первого, приложение было переведено в полный режим R8, что позволило провести дальнейшую оптимизацию. Стандартные библиотеки, такие как Retrofit, Firebase и Jetpack Compose, работали с R8 без проблем благодаря встроенным правилам. Чтобы перенести остальную часть огромной базы кода, разработчики устранили определенные пути выполнения с большим количеством рефлексии:

Чтобы перейти на полный режим R8, команда применила правила сохранения для потребителей из последнего выпуска Gson, обеспечив бесперебойный синтаксический анализ JSON. Они также решили проблемы оптимизации с помощью динамических прокси-серверов Java, которые используют рефлексию для генерации реализаций интерфейса во время выполнения. Поскольку R8 не может обнаружить это использование во время компиляции, он удаляет или переименовывает необходимые классы, что приводит к сбою приложения. Вместо того чтобы применять общие исключения, которые увеличили бы размер приложения, команда изолировала определенные классы, использующие рефлексию, и реализовала узконаправленные правила сохранения, обеспечив стабильность приложения и эффективность оптимизации.

В итоге полный режим R8 обеспечил значительные улучшения, в том числе дополнительное уменьшение размера скачиваемого приложения на 14 %, сокращение времени запуска на 10% и снижение количества ошибок ANR в активном режиме на 10 %. После перехода на R8 Full Mode база кода Grab достигла следующих показателей оптимизации R8:

  • Показатель оптимизации: 72%
  • Показатель сжатия: 73%
  • Показатель обфускации: 79,6%

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

Этап 3. Уточнение правил хранения 

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

Используя полный режим R8 и методично удаляя устаревшие правила сохранения, разработчики Grab добились значительных улучшений по всем трем основным параметрам, оцениваемым анализатором:

  • Показатель оптимизации: увеличивается с 72% до 78,2 %.
  • Показатель сжатия: увеличивается с 73% до 79,1 %.
  • Показатель обфускации: увеличивается с 79,6% до 81,7 %.

Помимо улучшения этих диагностических показателей, этот специальный этап уточнения правил сохранения позволяет дополнительно уменьшить размер скачиваемого приложения примерно на 3,6 %. Что ещё важнее, отслеживание этих показателей дает команде разработчиков объективные данные, необходимые для внедрения автоматических мер защиты CI перед слиянием. Это гарантирует, что новый код будет соответствовать строгим стандартам правил сохранения, а достигнутые улучшения производительности не будут утеряны со временем.

Результаты

Основные улучшения производительности после внедрения оптимизатора R8:

Этап оптимизацииANRРазмерХолодный запуск
Этап 1. Оптимизатор R8 (выпущен в 2025 году)~25 %Снижение на 16 %~27 %
Этап 2. Полный режим R8 (выпущен в 2026 г.)Снижение на 10 %Снижение на 14 %Улучшение на 10 %
Оптимизация правил хранения-Снижение на 3,6 %-
Общие (совокупные)~32,5 %~30,4 %Улучшение примерно на 35 %

Технические проблемы 

Включение оптимизатора R8 в базе кода Grab, которая создавалась в течение десяти лет, привело к серьезным техническим проблемам:

  • Тестирование. За десять лет накопилось много отражений, которые при полной оптимизации создавали пограничные случаи. Поскольку исчерпывающее тестирование миллионов строк кода было невозможно, команда выбрала поэтапный подход с высоким приоритетом. Тестирование было сосредоточено на коде, охваченном сужаемыми правилами сохранения. Эти правила итеративно уточнялись, чтобы нацеливаться только на определенные классы, методы или поля, необходимые для рефлексии, и максимально оптимизировать R8.
  • Задержка сборки и гибридная архитектура. Из-за гибридной системы сборки Grab (Bazel для отладки, Gradle для конечных сборок R8) экспериментальные изменения R8 требовали до двух часов в удаленной непрерывной интеграции. Чтобы устранить разрыв в обратной связи, Grab согласовала свои ежедневные сборки QA с поведением R8 в рабочей среде и автоматизировала сбор ключевых артефактов сборки CI, таких как mapping.txt и configuration.txt, для упрощения исследования.
  • Медленные циклы отладки. Поиск основных причин в сотнях оптимизированных классов создавал большую нагрузку на небольшую команду. Чтобы решить эту проблему, разработчики Grab автоматизировали загрузку файлов сопоставления в Firebase Crashlytics через Gradle и добавили сопоставления деобфускации для Play Console. Это позволило мгновенно и точно определять символы для проблем, возникающих во время выполнения.

Долгосрочное влияние R8 на код

Помимо немедленного повышения производительности, включение оптимизатора R8 дало постоянное структурное преимущество для огромной базы кода Grab. Специалисты Grab использовали инструмент с открытым исходным кодом app-sizer, чтобы оценить влияние R8. Они обнаружили, что R8 навсегда снизил размер скачиваемых файлов для каждой строки кода в приложении.

ANDDM_Grab_Inline_Metric.png

В эпоху разработки с помощью ИИ, когда объем кода быстро растет, эта оптимизация становится ещё более важной. Без R8 расширение кода пропорционально увеличивает размер скачиваемого файла. R8 обеспечивает ту же скорость расширения кода, но при этом значительно снижает объем потребляемой памяти.

Примечание. Методология: вклад Kotlin/Java в размер скачиваемого приложения (типичное устройство), разделенный на количество строк кода Kotlin в рабочей версии (cloc; только основные наборы исходных файлов; модули тестирования, макетирования и примеры исключены).

Управление правилами и ограничениями Keep

Чтобы предотвратить ухудшение правил и поддерживать оптимизацию R8, Grab планирует интегрировать автоматические ограничения перед слиянием непосредственно в конвейер непрерывной интеграции. В соответствии с этими правилами новые правила хранения строго ограничены, а любые дополнительные правила требуют явного обоснования и проверки. Команда Grab отслеживает показатели оптимизации R8 в CI. Команда планирует использовать эти показатели для непрерывной обратной связи, чтобы выявлять регрессии до слияния и сохранять оптимизацию в каждом цикле выпуска. 

R8-rules-score (1).png

Рисунок 1. Панель непрерывной интеграции, на которой отслеживаются показатели сжатия, оптимизации и обфускации R8 в сборках Grab. Это позволяет выявлять и предотвращать регрессии в разных выпусках.

Заключение 

Столкнувшись с проблемами, связанными с огромной и устаревающей базой кода, команда Pax App Core в Grab решила бороться с увеличением времени запуска и скачками производительности после выпуска. Для этого они внедрили профили базовой конфигурации и постоянно улучшали конфигурацию R8. Команда сократила количество ошибок ANR в активном режиме примерно на 32,5%, уменьшила размер скачиваемого приложения примерно на 30,3 % и улучшила время холодного запуска примерно на 35 %. При этом удалось значительно повысить эффективность масштабирования базы кода.

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

Чтобы узнать больше об R8 и начать работу с ним, посетите сайт - d.android.com/r8.

Автор:
Продолжить чтение