company_banner

Уменьшение размера React Native-приложения на 60% за несколько простых шагов

Автор оригинала: Hugo Grochau
  • Перевод
Я тружусь в компании Mutual. Она работает в Бразилии, в сфере равноправного кредитования. Мы помогаем заёмщикам и заимодавцам наладить связь друг с другом. Первые ищут хорошие ставки, а вторые — доходы, превышающие то, что может предложить им рынок. Наш продукт применяется широким кругом пользователей, мы работаем в большой стране. В результате наши приложения для iOS и Android, основанные на React Native, загружают на очень разные устройства.



Надо отметить, что основная масса наших пользователей устанавливает приложения на бюджетные устройства. Мы можем делать такие выводы, пользуясь данными библиотеки Facebook device-year-class. Эта библиотека, получив сведения о модели устройства, сообщает о том, в каком году это устройство считалось бы высококлассным флагманским телефоном. Например, самым популярным телефоном среди наших пользователей является Samsung Galaxy A10. Этот телефон, хотя он и выпущен в 2019 году, мог бы считаться флагманом лишь в 2013. Анализируя данные об устройствах пользователей, мы можем говорить о том, что 85% этих устройств можно было бы признать устройствами высокого класса лишь в 2015 году или раньше. Из-за этого мы предъявляем особые требования к оптимизации нашего мобильного приложения. Цель оптимизации заключается в том, чтобы даже пользователи со слабыми устройствами могли бы с удобством пользоваться нашим приложением.


Процент устройств, которые могли бы быть признаны флагманскими в определённом году

В этой связи мы обратили пристальное внимание на размеры приложения. В случае с его Android-версией это было 26.8 Мб. Хотя это — не такой уж и большой размер, это, определённо, больше медианного значения размера приложений наших пользователей. Этот показатель, по сведениям Google Play Console, составляет 16.3 Мб. Размер приложения может быть решающим фактором для пользователей, имеющих ограниченные тарифные планы, или для тех из них, на устройствах которых мало памяти, что заставляет пользователей тщательно выбирать приложения, которые будут у них установлены. В результате некоторые приложения приходится удалять. Это особенно важно в случае с приложением Mutual, так как заёмщики платят ежемесячные взносы через это приложение. Когда заёмщик деинсталлирует наше приложение, шансы того, что он вовремя сделает платёж, очень сильно падают. А это напрямую влияет на заработки инвесторов, пользующихся нашей платформой.


Размер приложения Mutual гораздо больше, чем медианный размер приложений наших пользователей

Размер приложения влияет не только на уровень деинсталляций. Размер влияет ещё и на коэффициент конверсии установок приложения. Вот хорошая статья об этом, написанная командой Google Play. В этой статье речь идёт о важности размеров приложения. В частности, там говорится о том, что каждые дополнительные 6 Мб размера APK-файла уменьшают коэффициент конверсии установок на 1%.

Там идёт речь и о том, что в развивающихся странах, где нормой являются бюджетные устройства, этот эффект проявляется даже сильнее. А именно, уменьшение размера APK-файла на 10 Мб на развивающихся рынках соответствует увеличению коэффициента конверсии установок примерно на 2.5%.


Увеличение коэффициента конверсии установок на каждые 10 Мб уменьшения размера APK-файла в разных странах (по внутренним данным Google)

Нас очень сильно мотивировало то, как уменьшение размеров приложения способно повлиять на уровень деинсталляций и коэффициент конверсии установок приложения. В результате мы принялись за работу, направленную на как можно более сильное уменьшение размеров приложения с учётом сохранения его удобства для пользователей. Первым шагом этого проекта был анализ официальных рекомендаций Google для Android-разработчиков.

Android App Bundle


Читая рекомендации, мы узнали о том, что самый простой способ уменьшения размера приложения заключается в использовании нового метода распространения приложений, который называется Android App Bundle (AAB). До этого момента мы распространяли приложение, собирая старый добрый файл Android Package (APK), который может быть запущен на большинстве Android-устройств, и загружая его в Google Play Console. А вот AAB-бандл содержит только скомпилированный код и ресурсы. В результате, при его загрузке, за генерирование оптимизированных APK-файлов для различных типов устройств, с учётом их спецификаций и архитектуры CPU, отвечает Google.

Получается, что внеся простое изменение в процесс сборки проекта, мы можем получить серьёзное уменьшение размера приложения, не прилагая к этому больше никаких усилий? Это слишком хорошо для правды!

После того, как мы почитали документацию, мы всего лишь поменяли сборочный скрипт React Native Gradle так, чтобы он, вместо текущего assembleRelease, запускал бы bundleRelease. Вот и всё, что нам понадобилось для создания AAB-файла. После ещё некоторых модификаций, внесённых в действие supply конфигурации Fastlane, касающихся автоматической выгрузки материалов прямо в Play Store, мы перешли на AAB, и новая версия нашего приложения появилась в Google Play Console.

Одно только это изменение привело к уменьшению размеров APK-файлов, передаваемых на устройства пользователей. Уменьшение составило от 9.1 до 12.4 Мб. Как оказалось, использование Android App Bundle — это действенная методика, которая, и правда, позволяет уменьшить размер приложения.


Старый APK-файл и новый AAB-бандл, использование которого приводит к тому, что размер приложения на разных устройствах может составлять от 14.4 до 17.7 Мб

Правда, тут стоит проявлять осторожность. Если вы используете React Native с Hermes, то вам может понадобиться обновить вашу зависимость soloader (подробности смотрите тут). Иначе есть риск отдать пользователем приложение, в котором будет содержаться критическая ошибка. Нам повезло, мы смогли выявить эту проблему в ходе тестирования альфа-релиза проекта. Но ошибка легко могла бы проскочить в продакшн, так как она не проявляется при локальном тестировании или при сборке APK-файла.

Оптимизация ресурсов приложения с использованием Android Size Analyzer


Следующим предложением по оптимизации приложений, которое мы нашли в документации, стало применение Android Size Analyzer. Это — инструмент командной строки, который анализирует Android-приложения и ищет возможности уменьшения их размеров. Мы запустили этот инструмент, воспользовавшись командой следующего вида:

size-analyzer check-bundle [BUNDLE].aab

В результате мы получили список больших ресурсов приложения и изображений, которые мы можем оптимизировать. Нам, кроме того, порекомендовали настроить ProGuard.


Отчёт size-analyzer

▍ProGuard


ProGuard — это инструмент для сжатия, обфускации и оптимизации Java-байткода. Мы пока не исследовали возможность применения этого средства, так как узнали о том, что оно может быть несовместимо с некоторыми Android-библиотеками. Так как мы стремились к тому, чтобы как можно быстрее уменьшить размер нашего приложения, и к тому, чтобы сделать это как можно проще, мы решили оставить этот метод оптимизации на будущее.

▍Большие ресурсы приложения


Запустив size-analyzer снова, с ключом -d, мы получили список ресурсов приложения, отсортированный по их размеру. Так как этому инструменту ничего не известно о том, как именно пользователи работают с приложением, он дал нам возможность самостоятельно принимать решения о том, какие ресурсы стоит удалить, а какие стоит загружать в приложение динамически.


Список больших ресурсов приложения, упорядоченный по их размеру

Первым и самым большим ресурсом в этом списке был JavaScript-бандл React Native. Сейчас мы не можем разделить этот бандл и загрузить его динамически. Но позже мы об этом подумаем. Далее в этом списке находятся большие файлы шрифтов (TTF) и изображений (JPG и PNG).

▍Ненужные изображения


Наше внимание сразу же привлекли огромные JPG-изображения, применяемые в Storybook. Мы пользуемся этой системой для разработки и тестирования компонентов. Это — 2 Мб мусора, который попадал в продакшн-версию проекта. Позорная ошибка! Когда происходит нечто подобное, мы чувствуем себя так, будто совершили серьёзную глупость. Но в сложном мире разработки ПО все совершают ошибки. Я верю в то, что если рассказывать о своих ошибках во всеуслышание, это поможет другим разработчикам учиться на этих ошибках. Есть вероятность того, что вы совершаете те же ошибки в том случае, если не анализируете структуру ресурсов приложения, размер которого постепенно увеличивается.

▍Шрифты


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

Ещё одна вещь, на которую мы обратили внимание, заключалась в огромном размере самих файлов шрифтов. Размер каждого из них составлял примерно 670 Кб. Это означало, что четыре шрифта занимают в несжатом бандле умопомрачительные 2.7 Мб. Но, к нашему счастью, существует инструмент, называемый FontForge, который позволяет глубоко анализировать и модифицировать файлы шрифтов. Воспользовавшись этим инструментом, мы смогли узнать о том, что основной причиной большого размера файлов шрифтов являются символы расширенной кириллицы и ненужные глифы. Мы могли спокойно всё это удалить, так как текстовая часть нашего приложения полностью написана на португальском языке. Благодаря этому изменению мы смогли уменьшить размеры файлов шрифтов с 670 Кб до 70 Кб — на 90%.


Примеры некоторых глифов, включённых в наши шрифты

Благодаря тому, что мы избавились от ненужных шрифтов и оптимизировали оставшиеся, мы смогли снизить размер приложения на 3.8 Мб. Это привело к приятному уменьшению общего размера APK-файла на 2 Мб.


Список шрифтов и их размеры до оптимизации


Список шрифтов и их размеры после оптимизации

▍Оптимизация изображений


Проанализировав изображения, которые ещё оставались в приложении, мы обратили внимание на то, что некоторые из них довольно-таки велики. Мы обработали несколько таких изображений средством для оптимизации графики TinyPNG и увидели значительное уменьшение размера этих изображений. После этого мы решили оптимизировать все JPG- и PNG-изображения, используемые в приложении. Всего их было 41.


Изображение до и после оптимизации

Это дало нам возможность сократить размеры изображений с 2.5 Мб до 756 Кб, то есть, на 70%. Правда, из-за того, что изображения раньше оптимизированы не были, они, в процессе создания итогового APK-файла, сжимались. Оказалось, что наша оптимизация привела к уменьшению размера приложения, загружаемого пользователем, лишь на 500 Кб.

После этого мы поняли, что уже истощили все возможности быстрой оптимизации. Продолжение оптимизации ресурсов потребовало бы либо больших усилий, либо дало бы лишь незначительные улучшения.

Оптимизация JavaScript-бандла React Native


После того, как мы разобрались с ресурсами приложения, специфичными для платформы Android, пришло время проанализировать JavaScript-бандл. Оптимизацию бандла можно счесть хорошей идеей по трём причинам. Во-первых, это уменьшает размер готового APK-файла. Во-вторых — это приводит к более быстрому запуску приложения, так как виртуальной машине JS приходится обрабатывать меньший объём кода. И наконец, что самое важное, это ускоряет OTA-обновления, которые мы делаем несколько раз в неделю, пользуясь CodePush.

▍Анализатор бандла и оптимизация кода


Для того чтобы принять решение о том, как уменьшить размер бандла, сначала, надо было разобраться с тем, что в бандле занимает больше всего места. Для этого мы воспользовались отличным опенсорсным инструментом react-native-bundle-visualizer. Проанализировав с его помощью проект, мы получили визуализацию, в которой присутствовала каждая папка и каждая зависимость приложения с указанием размеров соответствующих сущностей.


Снепшот библиотек и папок кодовой базы фронтенда приложения Mutual с указанием размеров

Мы выяснили, что общий размер бандла составляет 5.49 Мб. 57.8% этого объёма представляют зависимости из папки node_modules, 27.5% — код приложения. То, что осталось, используемому нами инструменту идентифицировать не удалось. В ходе процесса сборки бандла неиспользуемый код уже удалялся, поэтому то, что мы видели, представляло собой код, который реально используется приложением. Но, даже учитывая это, в бандле всегда можно найти что-то такое, что можно улучшить.

Самая большая из зависимостей проекта — math.js. Эта библиотека, как можно судить по её названию, реализует множество математических операций. У нас нет особой необходимости в этой библиотеке из-за того, что мы выполняем все важные вычисления на сервере, после чего просто отправляем результаты приложению. Когда мы присмотрелись к коду приложения, оказалось, что библиотека используется лишь для выполнения некоторых простых операций. Она, вероятнее всего, использовалась разработчиком, который работал и над серверным кодом, лишь в силу привычки. Мы быстро извлекли из библиотеки соответствующие методы и встроили их в нашу кодовую базу, полностью избавившись от этой зависимости. Это позволило уменьшить размер бандла до 4.64 Мб. Отказ лишь от одной библиотеки позволил уменьшить размер бандла на 15.5%!

Как уже было сказано, мы, для разработки и тестирования компонентов, используем Storybook. Правда, соответствующие возможности должны быть доступны лишь в локальном и промежуточном окружениях. Конечным пользователям это не нужно. Благодаря этому мы воспользовались переменной ENVIRONMENT для того чтобы управлять включением соответствующей части приложения. Хотя это и подходит для ограничения доступа, бандлер не может знать о том, какое значение записано в эту переменную. Из-за этого ограничения весь код Storybook попадал в продакшн-бандл.

Для того чтобы исправить проблему, мы изолировали импорт этого раздела в пределах одного файла. Затем мы создали две версии этого файла: одну, которая включает Storybook, и другую, предназначенную для продакшна, содержащую лишь макет компонента. Для переключения между этими файлами при подготовке продакшн-версии бандла, мы написали скрипт, который выполняется перед сборкой проекта и меняет один файл на другой. Благодаря этому методу мы смогли полностью убрать код Storybook из продакшн-версии приложения, убрав и зависимость из node_modules, и обычный код, касающийся конфигурирования «историй» Storybook.


Обновлённая папка Storybook с двумя версиями файла index

Эти два изменения позволили сократить размер бандла с 5.49 Мб до 4.2 Мб. А это означает, что, кроме прочего, приложение будет быстрее загружаться и быстрее обновляться.


Размер итогового бандла составил 4.2 Мб

После всех этих улучшений мы снова загрузили приложение в Play Store. Теперь нам сообщалось о том, что размер готового APK-файла будет находиться в диапазоне от 10.5 до 13.7 Мб. Это, учитывая то, что изначально приложение имело размер в 26.8 Мб, означает просто невероятное сокращение размера проекта примерно на 60%! А значит, мы, как сказано в статье команды Google Play, вполне можем рассчитывать на то, что коэффициент конверсии установок увеличится на 3.75%.


Сравнение исходной APK-версии приложения с итоговой AAB-версией, в которой сделаны все вышеописанные улучшения

Итоги


Мы, как программисты, ориентированные на нужды бизнеса, знаем о том, что иногда компании, ради более быстрого развития проекта, вполне можно пойти на увеличение его технического долга. Это особенно актуально для молодых стартапов вроде Mutual, которые пытаются найти своё место на рынке. Но если не наблюдать за этим долгом, можно совершить досадные ошибки, вроде отправки в продакшн 2 Мб тестировочных изображений и неоправданного использования огромной библиотеки. Нельзя позволять техническому долгу выходить из-под контроля и доставлять неприятности.

Кроме того, часто бывает так, что разработчики просто упускают доступные им возможности по оптимизации проектов. Поэтому рекомендуется иногда критически анализировать свои проекты. Просто для того, чтобы проверить, не упущена ли какая-то возможность быстрой оптимизации размера, скорости, или какого-то другого аспекта приложения. У нас ушло всего два дня на то, чтобы проанализировать приложение, спланировать работу и внести в проект улучшения, которые позволили сократить размер приложения на 60%. Сложно придумать что-то другое, способное дать такие же результаты за столь короткое время.

А как вы оптимизируете свои React Native-проекты?

RUVDS.com
RUVDS – хостинг VDS/VPS серверов

Комментарии 9

    0
    Есть статистика, как все эти операции повлияли на установку и на деинсталяцию?
      0
      Со стороны пользователя могу заметить, что подобные оптимизации скорее влияют на репутационные риски. Я не променяю устраивающее меня функционалом приложение на другое только потому, что оно весит 30 Мб, а не 10. Но каждый раз обновляя приложение в google play я делаю у себя засечку, что какие-то приложения обновляются на 3 Мб, а какие-то на 93. И у меня не высокая лояльность к компании, которая клепает патчи по 100 Мб.
        0
        10 микро-патчей каждую неделю или пара стометровых мажоров раз в месяц, что выберете?)
          0
          Ни то не другое.
          Извините, но вопрос из разряда «что ампутировать руку или ногу ?». Любой здравомыслящий человек будет взвешивать все «за» и «против» и примет решение на основе собственных соображений. Если все-же попытаться ответить на ваш вопрос более развернуто, то тот кто имеет мощный девайс и ценит свое время выберет один большой патч раз в месяц, чтобы не читать спам в ленте сообщений. А бедный школьник, у которого каждый мегабайт на счету выберет микропатчи. Это я к тому, что на ваш вопрос нет универсального ответа.
          0
          У меня было пару пользователей, которые сказали, что выбрали моё приложение из-за меньшего размера в сравнении с аналогичными. Правда, они жаловались, что на их телефоне почти не осталось свободного места.
          Хотя, я считаю, что это редкость, так как оценка размеров установок разных приложений занимает достаточно много времени.

          Впрочем, я время от времени занимаюсь оптимизации размера. Но это больше для того, что бы успокоить внутреннего перфекциониста.
            0
            Я вот тоже «из этих», хоть проблем со свободным местом в телефоне нет совершенно — при установке нового и незнакомого мне приложения, после оценки рейтинга, решающим фактором является размер, и меня здорово раздражает, что если раньше в маркете размер приложения был на самом видном месте, то потом гугол его «припрятал» и приходится заморачиваться чтоб посмотреть. И дело тут не моей в жадности, а в том что если приложение имеет «три кнопки» и две функции «сложения и вычитания» — ну не должно оно весить 50мб, значит или разработчик некомпетентен, или в этом приложении много того, что я не хочу у себя видеть.
              0
              Тоже странный критерий.
              Размер приложения определяет не количество кнопок на морде, а внутренний функционал, который зачастую не видно пользователю.
              И это не говоря о том, что размер указан в архиве, а сколько будет места занимать на телефоне там не указано. А ещё есть кеш, рабочие файлы, скачивание и прочее.
              Я понимаю, что это просто пример, но обычно приложения более сложны для такой оценки размера.

              Но если взять более реальный пример: например, есть три похожих приложения, которые весят 16, 20 и 22. По описанию они только очень примерно похожи и вроде бы дают более-менее нужный функционал.
              Какой будет выбор из этих трёх? Мы пока по размеру юзабилити ведь тоже не можем определить.
                0
                Да, всё верно, но не до конца.

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

                Приведу свой пример, правда он связан не с мобильными приложениями, а с десктопными. Понадобилось мне сделать простую приложуху, формочка там, пару кнопочек, пару запросов на сервер, пару ответов. Ничего особенного. Стал я искать информацию — на чем же сделать, и о чудо, веб технологии за пределами веба! Открыл я для себя Electron, обрадовался мол изучать с нуля ничего не надо. Да еще и «лидеры рынка» его используют, Skype например на нём, VScode, Atom. Стал смотреть глубже и опа-па, а приложение то, которое еще ничего толком не делает, а уже 100+мб весит, и тащит в себе Node.js. Шок. Посмотрел другие подобные варианты — не особо лучше. Понял что бред это всё. Решил что придется смотреть на нативные для системы языки. Остановился на С#. Стал ставить майкрософтовскую студию — сожрала 25-30 ГБ места. От шока отошел не сразу. Да, стартовое приложение получается считанные килобайты (чисто форма), но этот .net фреймворк… на 7-ке один, на 8-ке другой, на 10-ке — 10 различных вариантов… Стал еще копать — открыл для себя достойный опенсорсный аналог Delphi — Lazarus. На Делфи никогда не писал и вообще являюсь фанатом С-подобных ЯП, но решил попробовать. Установил среду разработки — всего 1 ГБ заняла места, сделал стартовый проект — пустая форма — 25 мб, печаль… Стал разбираться — можно ли что-то с этим сделать — и о чудо, путем несложных манипуляций это же самое приложение с 25 мб превращается в 1,6 мб! И запускается почти на любых версиях Win ОС, да еще и вроде можно скомпилировать под другие ОС.   

                Зачем я это всё писал. Я не спорю что глупо оценивать только по размеру, но для меня это остается важным фактором — что разработчик об этом задумывается. Вот есть например три приложения, у трех ± одинаковый рейтинг, три выполняют то что мне нужно, и даже чуть-чуть больше. Я не могу определиться какое лучше. И вот тут уже решает размер — одно приложение весит 3 мб, другое 5 мб, а третье 30 (почему? никаких глобальных отличий по функционалу от предыдущих — не заметил). Это я говорю про реальную ситуацию с которой сталкивался не раз. Конечно я выберу то что 3 или 5.
                  0
                  Я не говорю про крайности когда почти все приложения по 5, а одно 100 с вроде бы тем же функционалом. Тут слишком очевидно, но это бывает очень редко (я сам лично не встречал, но могу представить такой вариант).
                  Я говорю про более-менее реальность когда разброс в пределах 20-50%.

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

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое