Хабрахабр

[Из песочницы] Как собрать аналитику и не убить производительность

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

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

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

Попытка №1

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

Чтобы измерить подвисание, выбрали показатель FPS (Frames Per Second), а для измерения показателя — FPS-meter TinyDenser. Каждый воспринимает подвисание по-разному, так что мы нуждались в фактах и доказательствах. После подключения утилиты команда получила объективное подтверждение проблемы — показатель падал, временами достаточно заметно: меньше 30 кадров в секунду, запись экрана с включенной утилитой показана на Рисунке 1.

image

Рисунок 1. Запись экрана до оптимизации

Попытка №2

А если отложить отправку событий, пока пользователь скроллит список? «Хммм», — подумала команда, и решила создать очередь из событий и отправлять их после того, как скролл остановится. Тут все просто: добавляем OnScrollListener в RecyclerView и ждем, пока newState будет ровным SCROLL_STATE_IDLE — задача частично решена.

class IdleStateScrollListener(private val analyticsFacade: AnalyticsFacade) : RecyclerView.OnScrollListener()
}

Следующий шаг — реализовать накопление событий и их отправку.

Для периодической отправки выбрали ScheduledExecutorService с одним потоком, который запускал Runnable каждую секунду, время подобрали эмпирическим способом. Для управления очередью создали класс, который отвечал за добавление событий в очередь и отправку аналитики в сервис.

В принципе, задача была решена, на Рисунке 2 видим результат работы приложения после второй попытки. Это сразу дало результаты, значительный прирост FPS. Но осталась одна задачка — события отправлялись в сервис, что приводило к частому генерированию объектов класса Intent, а это дополнительно нагружало GC и доставляло «приятности» в виде stop-the-world пауз.

image

Рисунок 2. Запись экрана после второй попытки

Попытка №3

«Отправка не одного события за раз, а списка событий — еще одна чудесная идея», — подумала команда. После короткой доработки класса команда реализовала отправление событий списком и получила стабильные значения показателя на уровне 55–60 кадров в секунду (Рисунок 3).

image

Рисунок 3. Запись экрана после третий попытки

Выводы

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

Можно ли было сделать еще что-то?

Наша команда остановилась на третьем варианте, но это не единственное, что можно было применить.

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

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть