Author: The technique of idle fish — not song


background

Main business currently idle fish (search, details, etc.) are hosted by the Flutter, although Flutter is high performance technology solutions across the end, but as the business rapid iteration and complexity, application performance is not satisfactory, mainly reflected in the slow start, slow page load, sliding operation is not smooth, caton page, etc., It will affect the user experience. For this reason, we launched the project of Xianyu Experience upgrade. Starting from the key nodes of users’ operation path of Xianyu App, we systematically optimized the corresponding application, including sliming installation package, speeding up the startup speed, optimizing page loading time and fluency, and finally improving users’ experience. This paper mainly introduces the performance experience optimization of idle fish on Flutter side, which is mainly divided into three parts:

  • Fluency optimization
  • Search results page load optimization
  • Details page load optimization

The optimization scheme is shown as follows:


Fluency optimization

Start with the thorniest problem of Flutter smoothness. Judging from online data and physical sensation in actual use, the sliding smoothness of the search page and product details page is not satisfactory. There are also a lot of feedbacks about lag in public opinion. Next, fluency optimization is introduced in three aspects:

  • Construction of the Flutter Caton positioning tool
  • Long list loadMore optimization
  • Scroll to load the minigraph

Construction of the Flutter Caton positioning tool

Flutter adopts a data-driven approach to building the UI, according to the official Flutter Performance Best Practices

  • Avoid repetitive and time-consuming work in the build() method, because the build() method of the child Wdiget will be called frequently when the parent Widget is rebuilt
  • Avoid returning an overly large Widget in an excessively long build() method. Break them up into widgets and encapsulate them

But as the business iterates quickly, developers tend to slip up and write poorly performing code. Ideally, a performance analysis tool would automatically locate problematic code and modify it in time. Flutter provides Devtools for performance analysis. Its Timeline interface can analyze application UI performance frame by frame and CPU profiler interface can capture time-consuming actions during the lag process. During the development phase, we can use this tool to analyze locally reproducible holdup problems. However, the limitations of Devtools are:

  • It can only be used to analyze the problem after the occurrence of the problem, but can not monitor the problem.
  • It can only be used for offline investigation. There are not enough scenes for online feedback.

To this end, we need to build a Flutter tool, which can be used to monitor and locate the time function caused by the lag online and offline. Implementation idea: We know that Dart code in release mode is compiled based on AOT, and both business code and SDK are compiled into platform-related machine code. Therefore, we can capture native stack through signal mechanism, and then obtain the flutter stack through symbolic restore.

We used the Caton tool to locate and troubleshoot two types of problems causing low fluency: time-consuming functions and overrendering.

Positioning time function

For the lag caused by the time-consuming function, you can locate the time-consuming function by looking at the stack.It can be seen from the call stack that data serialization and deserialization are time-consuming during channel invocation. By looking at the code of FxImage, we know that the native implementation of resumeImage is empty, so the channel call can be omitted on the flutter side.

Location overrendering

A large amount of data reported in the automation phase is the stack shown below in the rendering phase, which cannot be located to the business code.

This is because the refresh mechanism of Flutter is a centralized and asynchronous rendering mechanism. The business layer needs to refresh the interface elements. The framework layer will mark the elements that need to be updated dirty first and then wait for the next rendering callback on the engine side. This mechanism results in a large number of elements in the stack during the fetching and rendering stage. Rebuild and update methods are function call stacks of the flutter framework, but without the business-side code, only looking at these stacks cannot help us locate the lag problem.

The idea is that by adding dirty element information to the rendering process, the complexity of the dirty element (we calculate complexity based on element depth, length, number of child nodes) can help us detect if there are code irregularities that cause the refresh range to be too large.

With this scenario, we navigate to the detail page to quickly ask the component overrendering problem:

Viewing the dirty element information, you can see that the complexity of the dirty element is high. Optimization: improve build efficiency by sinking setState refresh data into leaf nodes, extracting each TAB into LabelStatefulWidget, and only doing partial refresh.

Long list loadMore optimization

Through the Caton tool, we found that the whole list container would be dirty and reconstructed in the process of sliding the search results page list more, resulting in serious fluency problems. After analysis, setState will be called to dirty the container widget because more data is returned after the list data is appended. And because we did preloading, more logic was triggered to load in the first few screens of the slide to the bottom, resulting in a higher frequency of dirty rebuild widgets. In the first phase, we started from the business layer, and did not call setState after preloading more data, but saved it to memory first, and then called setState after sliding to the bottom to rebuild the list container. Later, we optimize from the bottom layer of the list container. Loadmore will not be dirty and the whole list container will be rebuilt. The loading and sliding process of the container has been greatly optimized and the experience is obviously improved.

Before optimization, you can see that the entire container has been refreshed (red area shows dirty widgets).

After optimization, only new cards are refreshed

Scroll to load the minigraph

Feeds stream cards contain a large number of images. Loading a large number of images in the process of fast sliding is a big test for memory and IO, affecting fluency, especially on low-end phones. Optimization idea: In the process of fast scrolling, only load the fuzzy image with small size, wait until the scrolling stops and then gradually show the original image, and do not load the original image in the area beyond the screen to optimize the on-screen experience. The effect is shown below :(to illustrate the effect, a 5x smaller image is used here)

summary

After optimization, the fluency FPS of the Details page and search page of Idle Fish increased by 3 points, and the lag times of low-end phones decreased by half, while the fluency of mid-high-end phones increased to 57 or above, and the lag times approached zero.

The online high availability FPS data is as follows:

The horizontal axis represents the frame rate range, and green indicates the optimized version. The further to the right of the curve distribution, the better the fluency.

FPS curve of online high-end machine. Green indicates the optimized version

Search for high available FPS data on the page line as follows:

Line low end FPS curve. Green indicates the optimized version

FPS curve of online high-end machine. Green indicates the optimized version


Search results page load optimization

After optimizing the fluency issue, let’s look at what needs to be optimized for page loading. Before optimization, there is a long loading process from searching keywords to displaying search results. For page loading speed optimization, we start from the business process to find a breakthrough, the opening process of the search results page is as follows:

The search result page is implemented by the Flutter, but it is clicked to open from the Native page. In the context of the mixed stack, it takes some time to intercept the route to open the container. We can carry prefetch information through URL, and conduct asynchronous parallel data prefetch at the same time when jump navigation is performed in Native, which can reduce the time of page opening. At the same time, because the RT of search page request is relatively high, generally the page comes in, but still waiting for the network request to come back, so if the template is preloaded when the network request comes back, the probability is high.

The optimized process is as follows:

By some parallel means, we optimized the loading time of search result page by 300ms on Android low-end machine by using the scheme of data prefetching and template preloading. At the same time, skeleton screen animation (Lottie implementation) is displayed in data request instead of small yellow croaker loading, bringing better user experience.

Before optimization:

After the optimization:


Details page load optimization

For the loading optimization of the detail page, we mainly optimize it by the following three means:

  • FlutterBoost optimization
  • Data passthrough
  • Animated transitions

FlutterBoost optimization

Currently, Xianyu is a hybrid development mode of Native+Flutter, which handles the mapping and jump of mixed stack pages through FlutterBoost. In the open process of FlutterBoost, a new container is opened via startActivity(), and most of the jump scenes of the detail page are from the Flutter page. In fact, the previously opened container can be reused. For this application, we added a new feature in FlutterBoost. When a Flutter page opens a new Flutter page, You can select two Flutter pages to share a Flutter container (Activity, ViewController) to speed up page opening, reduce memory consumption, and support the animation of Flutter page switching.

Data passthrough

Jump in the search results page for details and guess you like to dance for details of the scenario, the details of data through an interface on the can get, we can put this part of the data passthrough to the details page, in the process of request of detailed data, first show simple content, such as the main figure, title, price, detailed data, such as after you come back to update the details page, straight out of the experience.

Animated transitions

On the basis of the previous data transparent transmission, we also realized the effect of immersive page switching through the transition animation to further improve the user experience.

ModalRoute inherits ModalRoute to take over buildPage process during route navigation, animates the simplified AnimateDetailPage, monitors the animation state, and then hangs the DetailPage on the widget tree after the animation is completed.

Before optimization:

After the optimization:


Conclusion outlook

To sum up, in the upgrade of Flutter performance experience, we optimized the main page loading time and smoothness in terms of technical details. In addition to business process optimization, we also accumulated some general optimization capabilities. Such as data prefetch, screen loading in widget frames, optimization for loading more long lists, local refresh capability for long lists, optimization for long list scroll differentiator algorithms. In terms of visual optimization, the theme of the Flutter is young and the design uses a large number of micro-animation elements. The Flutter Lottie animation is used everywhere on the page, such as the release page, the search result loading page, and the loading and refreshing animation. After optimization, Xianyu App becomes more smooth and silky.

In the future, we need to explore and think more about Flutter engine startup, especially after the homepage switch to Flutter, optimization of Flutter startup is a must. In addition, due to the rapid iteration of the business, the early optimization work can easily deteriorate later. How to meet the efficiency and ensure the performance while the business is changing rapidly is the next problem to be solved. For example, before code integration, the substandard code is sent back through the performance bayonet and optimization suggestions are given. Build performance tuning tools into the container framework layer.