В данной статье разберем как можно автоматизировать процесс обнаружения регрессии размера андроид приложений.
Мы, как разработчики, хотим сделать наше приложение доступным многим пользователям, чтоб они могли скачивать и устанавливать наше приложения без каких-либо проблем. И одна из метрик связанный с этой целью является размер нашего приложения - большой размер приложения может послужить причиной проблем при загрузке и установки на устройствах, с ограниченным размером памяти или медленным интернетом. И чем больше мы уменьшаем размер нашего приложения, тем выше становится конверсия установок, как подробно сказано об этом в отдельной статье. Поэтому нам бы хотелось не только оптимизировать размер, но и иметь мониторинг изменения размера и могли обнаружить увеличение размера до того, как это попало в production.
Когда речь идет о большой команде, очень тяжело проконтролировать изменения происходящие в проекте, так как много feature команд, которые добавляет крутые фичи, которые в последствии могут повлиять на размер. Поэтому лучше всего этот контроль сделать автоматизированным, и в этом нам поможет наш специальный flow на CI.
Обнаружение регрессии в development
Начнем мы с самого простого, будем проверять размер приложения в development ветке, так как она выступает единственным источником правды для нас, так как мы создаем наши релизные ветки именно от development.
Нашей первой идеей было - что если собирать релизный билд на каждый commit(pull request merge) в development и собирать метрики размера. Но сразу поняли что это не лучшее решение для быстрого решения этой задачи, так как в случае, если 2 коммита одновременно запушатся(push) в development, то тяжело будет сравнить эти 2 apk между собой, так как релизный билд занимает приличное время. Поэтому мы и нашли способ по проще.
1. Предположим, мы выберем определенный коммит в определенное время и запустим с него релизный билд, знакомой нами gradle командой:
./gradlew assembleRelease
И в результате, с полученного apk, мы собираем и выгружаем метрики(размер apk, текущий использованный commit, версия приложения, дата и тд например в формате json) в любое вам хранилище(gcloud), главное чтоб можно было туда загружать наши артефакты и загрузить к себе обратно, когда будет нужно. Также загружаем собранный релизный apk, он нам будет нужен в последующей проверке размера приложения. Используем gsutil command line tool:
gsutil cp "$build_size_info.json" "gs://custom_folder/build_size_info.json"
gsutil cp "$current-release.apk" "gs://custom_folder/current-release.apk"
2. Выбираем следующий определенный commit в development, через некоторые время, к примеру, через 3 часа(на свой выбор). Также собираем релизный билд на этом commit, все также загружаете apk, метрики. Но теперь нам нужно загрузить из хранилища предыдущий билд, который мы собирали 3-мя часами ранее, и сравниваем предыдущий и текущий apk. Но просто сравнение размеров apk нам недостаточно, хотелось бы подробное сравнение, в котором мы сможем выяснить, какие файлы были добавлены или удалены. В этом нам поможет diffuse tool by Jake Wharton, который может сравнивать apk, aab, jar, aar файлы и показать детальную информацию сравнения.
java -jar diffuse-binary.jar diff $prev.apk $current.apk > $report.txt
gsutil cp "$report.txt" "gs://custom_folder/report.txt"
Результат сравнения записываем в файл report.txt
и загружаем в наше хранилище, чтобы далее можно было по ссылке посмотреть. Если показать содержимое report.txt
, то будет выглядеть вот так:
Файл будет также содержать больше деталей про измененные ресурсы(добавленные, удаленные), но нам может быть достаточно и этой информации, чтобы понять что послужило регрессией. Нас интересует столбец diff
, который показывает на сколько были изменены файлы внутри apk. В данном случае, мы видим что значительно был изменен файл arsc
(resources.arsc), который хранит в себе все компилируемые ресурсы(drawable/.xml, layout/, raw/, values/ etc.). И мы понимаем что кто-то замержил в development Pull request, в котором были добавлены компилируемые ресурсы и мы можем найти этот commit в git историй.
3. Чтоб облегчить задачу поиска коммита, который послужил регрессией, мы можем собрать все вовлеченные коммиты между коммитами когда был проверен размер приложения. Для этого вызываем команду git:
git rev-list --ancestry-path "$prev_commit_hash".."$current_commit_hash"
Итог:
В результате, наш workflow на CI будет выглядеть так:
Чтобы запускать проверку каждые 3ч, вы можете назначить задачу в вашем CI, либо запускать в зависимости от времени(зависит от возможностей вашего CI).
Если обнаружена регрессия размера, можете оповестить команду со ссылкой(report.txt
в gcloud) на детальную информацию сравнения, в последствий ответственный разработчик может выявить причину и найти конкретный commit в development, который послужил регрессией и исправить, либо ревертнуть изменения.
Недостатки
1. Позднее обнаружение регрессии - узнаем о регрессии только после того, как изменения уже были влиты в development
2. Требует ручной работы - все таки это не полностью автоматизированный процесс, так как, как только происходит регрессия, разработчику приходится проверять результат сравнения и искать конкретный commit, внутри всех вовлеченных коммитов.
3. Собираем универсальный apk - который содержит ресурсы для всех конфигураций девайсов, что влияет на размер приложения. Эта проблема решается внутри Google Play, куда нам надо загрузить App Bundle, а внутри собирается apk в зависимости от конфигурации конкретного устройства.
Все эти недостатки мы будем решать в следующей части статьи.
Zara6502
я правильно вас понял что увеличение размера apk файла на 105 Кб вы воспринимаете как "шеф, всё пропало"? а вы могли бы пойти работать в сбербанк? их приложение пухнет безостановочно, когда доросло до 2,5 Гб и я пожаловался в СБ они просто сказали переустановить приложение ) (да, я знаю что это не apk а кеш программы, но и 2,5Гб данных для работы им не нужно это точно)
akniyetc Автор
Здесь имеется в виду именно размер загружаемого из google play apk(download size), поэтому 105Кб в течении 3ч. это значимая цифра, так как apk обычно все ресурсы хранит оптимально(помимо не компилируемых ресурсов). Насчет контроля размера кэша приложения, я думаю мало кто об этом заботится, и сомневаюсь о реализуемости, даже если реализуемо, то стоит ли оно того. Зачастую это решается простой очисткой кэша.
Zara6502
что значит "в течение 3ч"?
Вы предлагаете пользователю следить за размером кеша и чистить его самостоятельно? А разработчики софта не могут написать одну строчку if(cache.size>1M) cache.flush при старте программы?
mSnus
Это же паттерн "Барахольщик": - а давай выкинем старье с балкона? - нет, вдруг там что-то ценное или пригодится! И в результате проще устроить капремонт (сбросить весь кэш), чем разбираться, что нужное, а что нет.
akniyetc Автор
Это примерный промежуток времени(3 часа), для проверки размера, у каждого может быть свое, но суть в том, что для изменении произошедших в течении последних 3 часов - 105Кб это значительное изменение. Значение промежутка времени и значение лимита для размера можете задавать сами. В нашем случае, мы проверяем размер каждые 3 часа, с лимитом 75Кб(для примера).
Думаю это не так просто будет реализовать, так как в кэше могут храниться и важная информация, которую лучше не стоит очищать. А выборочное удаление - это уже другая задача. Но я согласен, что потребление такого большого места в памяти банковских приложений, может быть неоправдано. Но опять же, тяжело сказать как они именно используют память внутри. Кэш приложения это отдельная тема и заслуживает отдельной статьи думаю.
Zara6502
почему?
учитывая что мне предлагают его очистить без опасений уверен что там нет ничего ценного, по всяком случае для СБ.
akniyetc Автор
относительно значительное*
Например в играх такое значение не существенно.
если вопрос, почему у нас в приложений это так, то лимит в 75Kb мы определили на начальном этапе. Приложение доставки еды, обычно не требует колоссальных ресурсов, да и нужно постараться превысить лимит за такой короткий срок. Этот лимит не говорит, что "Тревога! Все пропало!", а лишь сигнализирует, что где-то не хватает оптимизаций, и дает знать что и как было изменено.