How to collect analytics and not kill performance

Analytics is an integral part of a modern mobile application. Analytics allows you to collect information about the user to develop and improve the product.

Often, gathering information reduces application performance. The process additionally loads the CPU and memory, and this is a high price. Slow operation of the application can cause negative user feedback, lower the rating and lead to loss of audience.

This problem was faced by our team of Android developers while working on the next project that was related to the news. We needed to register the display of each news in the list.

Attempt # 1

Having received the task of collecting analytics, the team quickly showed the result. The trigger for generating the event was chosen by the onViewAttachedToWindow method . Everything seems fine, but with a quick scroll, the interface noticeably hung up - something went wrong. The problem had to be solved.

Everyone perceives a hang-up differently, so we needed facts and evidence. To measure the hanging, we chose the FPS indicator (Frames Per Second), and the FPS-meter TinyDenser to measure the indicator . After connecting the utility, the team received an objective confirmation of the problem - the indicator fell, sometimes quite noticeably: less than 30 frames per second, the screen recording with the utility turned on is shown in Figure 1.

Figure 1. Screen capture before optimization

Attempt # 2

And if you postpone sending events until the user scrolls the list? “Hmmm,” the team thought, and decided to create a queue of events and send them after the scroll stops. Everything is simple: add OnScrollListener to the RecyclerView and wait until newState is equal SCROLL_STATE_IDLE  - the task is partially solved.

classIdleStateScrollListener(privateval analyticsFacade: AnalyticsFacade) 
      : RecyclerView.OnScrollListener() {
    funonScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        analyticsFacade.setPending(newState != RecyclerView.SCROLL_STATE_IDLE)

The next step is to implement the accumulation of events and their sending.

To manage the queue, a class was created that was responsible for adding events to the queue and sending analytics to the service. For periodic submissions, we chose ScheduledExecutorService with one thread that ran Runnable every second, the time was chosen in an empirical way.

This immediately gave results, a significant increase in FPS. In principle, the problem was solved, in Figure 2 we see the result of the application after the second attempt. But there was only one problem left - the events were sent to the service, which led to the frequent generation of objects of the Intent class , and this additionally loaded the GC and delivered "pleasures" in the form of stop-the-world pauses.

Figure 2. Screen recording after the second attempt

Attempt number 3

“Sending more than one event at a time, but a list of events is another great idea,” the team thought. After a short revision of the class, the team implemented the sending of events in a list and received stable values ​​of the indicator at the level of 55–60 frames per second (Figure 3).


Figure 3. Screen capture after third attempt


The trivial task of collecting analytics has become an interesting and cognitive process, during which the team has mastered their skills in researching application performance problems and finding ways to solve them. I hope our experience will be useful for both beginners and experienced developers.

Could anything else be done?

Our team focused on the third option, but this is not the only thing that could be applied.

In the current implementation, when the onViewAttachedToWindow method is triggered, the news is transformed into an analytics event object, respectively, a new object is created. One of the possible solutions is to postpone the conversion until the moment of sending: to accumulate in the queue not the events, but the elements of the list themselves. Then the conversion will occur when the scroll will be in SCROLL_STATE_IDLE mode . Also for events it was possible to create a pool of objects. In combination, these actions can give an additional performance boost to the application.

Also popular now: