Как мы ускорили запуск приложения Dropbox для Android на 30 % +5


Запуск приложения – это первое впечатление наших пользователи после установки приложения. Это то, что происходит каждый раз. Простое и быстрое приносит пользователям гораздо больше радости, чем приложение, которое имеет массу функций, но требует вечности, чтобы запуститься. Команда Dropbox Android потратила время и силы на измерение, выявление и устранение проблем, влияющих на время запуска приложения. В итоге мы сократили время запуска приложения на 30 %, и вот история о том, как мы это сделали.


Страшный подъём 

Исторически сложилось так, что в Dropbox мы отслеживали начало работы приложения, измеряя, сколько времени прошло с момента, когда пользователь нажал на иконку нашего приложения, и до момента, когда приложение полностью загрузилось и готово к работе с пользователем. Мы абстрагировали измерения инициализации приложения таким образом:

perfMonitor.startScenario(AppColdLaunchScenario.INSTANCE)

// perform the work needed for launching the application

perfMonitor.stopScenario(AppColdLaunchScenario.INSTANCE)

Как управляемая данными команда мы отслеживали и контролировали инициализацию приложения с помощью графиков, которые могли увидеть все инженеры. На графике ниже показаны измерения процентиля 90 при запуске приложения в период с конца марта по начало апреля 2020 года.

Как видно на графике, время запуска приложения, кажется, не так уж сильно меняется. Небольшие колебания при запуске приложений, которые варьируются в пределах пары миллисекунд, вполне ожидаемы в приложении, с которым – на самых разных устройствах и ОС – работают миллионы пользователей.

Однако когда мы посмотрели на время запуска приложения с декабря 2019 года по апрель 2020 года, то увидели другую картину. Время запуска ползло вверх в течение нескольких месяцев по мере разработки и добавления функциональности. Однако, поскольку графики показывали нам только те изменения, что произошли за две недели, мы пропустили медленный рост времени запуска, который имел место в течение нескольких месяцев, а не двух недель.

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

Больше цифр 

Некоторые особенности приложения измеряются относительно просто, например, то, сколько времени занимает вызов API сервера. Другие особенности, такие как запуск приложения, измерить намного сложнее. Прежде чем пользователю будет показан главный экран приложения и оно будет готово к взаимодействию с пользователем, требуется выполнить множество различных действий. Изучив код инициализации, мы определили основные события, происходящие во время инициализации. Вот несколько из них:

  1. Выполнение миграции.

  2. Загрузка сервисов приложения.

  3. Загрузка первых пользователей.

Мы начали наше исследование с профилирования в Android Studio, чтобы измерить производительность наших тестовых телефонов. Проблема с профилированием производительности при таком подходе заключалась в том, что тестовые телефоны не давали статистически значимой выборки того, насколько хорошо на самом деле работает запуск приложения. Приложение Dropbox на Android имеет более 1 миллиарда установок на Google Play Store, охватывает несколько типов устройств, некоторые из них – старые Nexus 5s, другие – самые новые, лучшие устройства Google. Было бы глупо пытаться профилировать такое количество конфигураций. Поэтому мы решили измерить производительность разных этапов запуска приложения при помощи пошагового сценария в производственной среде.

Вот обновлённый код инициализации, где мы добавили логирование трёх упомянутых выше шагов:

perfMonitor.startScenario(AppColdLaunchScenario.INSTANCE)
perfMonitor.startRecordStep(RunMigrationsStep.INSTANCE)

// perform migrations step wrok

perfMonitor.startRecordStep(LoadAppServicesStep.INSTANCE)

// load application services step

perfMonitor.startRecordStep(LoadInitialUsers.INSTANCE)

// perform initial user loading step

perfMonitor.stopScenario(AppColdLaunchScenario.INSTANCE)

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

Мы нашли виновников

На приведённом ниже графике показано общее время запуска приложения с января по октябрь 2020 года.

В мае мы начали измерять все основные этапы инициализации приложения. Эти измерения позволили нам выявить и устранить основных виновников замедления запуска приложения.

В проблемах с производительностью оказались виноваты инициализация библиотеки Firebase Performance, миграция флагов функциональности и начальная загрузка пользователя.

Firebase Performance Library

Чтобы измерять и отправлять метрики о производительности приложений, в Google Firebase включена Firebase Performance Library. Она предоставляет полезные функциональные возможности: показатели производительности отдельных методов, а также инфраструктуру для мониторинга и визуализации производительности различных частей приложения. К сожалению, библиотека производительности Firebase также имеет некоторые скрытые издержки. Среди них – дорогостоящий процесс инициализации и, как следствие, значительное увеличение времени сборки. При отладке мы обнаружили, что инициализация Firebase Suite длилась в семь раз дольше, когда был включен инструмент Firebase Performance.

Чтобы устранить проблему с производительностью, мы решили удалить инструмент Firebase Performance из приложения Android Dropbox. Firebase Performance library – замечательный инструмент, который позволяет профилировать детали производительности очень низкого уровня, такие как отдельные вызовы функций, но поскольку мы не использовали эти возможности библиотеки, то решили, что быстрый запуск приложения важнее данных о производительности отдельных методов, и поэтому удалили ссылку на эту библиотеку.

Миграции

Есть несколько внутренних миграций, выполняемых каждый раз при запуске приложения Dropbox. Они могут включать обновление флагов функций, миграцию баз данных и т. д. К сожалению, мы обнаружили, что некоторые из этих миграций запускались при каждом запуске приложения. Ранее мы не замечали их плохой производительности, потому что приложение быстро запускалось на устройствах разработки и тестирования. К сожалению, этот миграционный код особенно плохо работал на старых версиях ОС и старых устройствах, что способствовало увеличению времени запуска.

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

Загрузка пользователя

Мы храним метаданные контактов пользователей Dropbox на устройстве в виде больших двоичных объектов JSON – это устаревшая часть приложения. В идеале эти большие двоичные объекты должны читаться и преобразовываться в объекты Java только единожды. К сожалению, код извлечения пользователей вызывался несколько раз из разных устаревших функций приложения, и каждый раз этот код выполнял дорогостоящий синтаксический анализ JSON, чтобы преобразовать кастомные объекты JSON в объекты Java. Хранение контактов пользователей в формате JSON как таковое было устаревшим решением в архитектуре и оно было частью легаси-монолита. Чтобы устранить эту проблему немедленно, мы добавили функциональность кэширования анализируемых пользовательских объектов во время инициализации. Мы продолжаем разбивать легаси-монолит, и более эффективным и современным решением для хранения контактов пользователей было бы использование объектов базы данных Room и преобразование этих объектов в бизнес-объекты.

Что делается сейчас?

В результате удаления ссылки на Firebase Performance и дорогостоящих шагов миграции, а также благодаря кэшированию пользовательских загрузок мы подняли производительность запуска приложений Dropbox Android на 30 %. Благодаря этой работе мы также собрали дашборды, которые помогут предотвратить деградацию времени запуска приложений в будущем.

Мы взяли на вооружение несколько методов, которые, мы надеемся, не позволят нам повторить ошибки с производительностью. Вот пара из них:

Мы смотрим, что происходит с производительностью, когда мы добавляем сторонние библиотеки.

Обнаружив то, насколько библиотека Firebase Performance замедлила запуск нашего приложения, мы ввели добавление сторонних библиотек как процесс. Сегодня, прежде чем библиотеку можно будет добавить и использовать в нашей кодовой базе, нам необходимо измерить время запуска, время сборки и размер APK-файла приложения.

Мы всё кэшируем.

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

Заключение

Больше вкладывая в убедительную аналитику и измерения производительности, теперь команда ещё больше ориентируется на данные , что помогает нам вкладываться в кодовую базу, например убирать устаревший код C++ гораздо более уверенно. Сегодня мы следим за несколькими дашбордами, которые дают нам представление о производительности наиболее важных частей наших приложений, и у нас есть процессы, которые обеспечивают быстроту приложения Dropbox, чтобы оно радовало пользователей.


Узнайте подробности, как получить Level Up по навыкам и зарплате или востребованную профессию с нуля, пройдя онлайн-курсы SkillFactory со скидкой 40% и промокодом HABR, который даст еще +10% скидки на обучение.

Другие профессии и курсы




К сожалению, не доступен сервер mySQL