We have found a highly compatible, low-cost, out-of-the-box homepage performance optimization method.

At the beginning of 2020, the complexity of the Xiaohongshu homepage UI increased significantly. While optimizing the layout xml and using some stub methods, we are also looking for some lower-cost and better-performing methods.

X2C was an optimization method well known in the industry at that time. Its principle is to translate xml into code during compilation, which can effectively avoid reflection and the loss of reading resource files. Since there are many custom View scenarios in the Xiaohongshu APP, X2C will also bring high maintenance costs.

After a time-consuming in-depth analysis of LayoutInflater, we found an APT solution that is compatible with various View scenarios. This solution avoids the loss caused by reflection and does not increase additional maintenance costs, making it a tool that can be used out of the box.

Our exploration was inspired by ViewCompiler . As an experimental tool from Google, ViewCompiler can manually convert xml layouts into java files or dex files, but it does not support merge and include tags.

ViewCompiler was introduced in Android Q (Android 10). It is still an experimental tool at present, so we usually have no way to use it. The picture below shows the source code in Android S (Android 12). You can see that this feature is not turned on.

The principle is also very simple. First generate a template code fragment, and then generate the logic code for traversing xml.

The main benefit of this is that it can save the time consumption caused by reflection. The official AppCompatViewInflater has already handled the creation of native Views. By directly matching the name new object, it avoids the performance overhead caused by using reflection.

In daily use, reflection performance overhead is mainly concentrated in the custom View part. Our App itself is a scene with many custom Views, so it is naturally suitable for this method of VIewCompiler. At the same time, because every attrs will be traversed when traversing xml, it also has a huge advantage in maintainability. We do not need to do any processing of custom attrs.

Based on reading the source code and generated code of X2C and ViewCompiler, we decided to make one that can generate Kotlin code and also solve the include and merge tags that ViewCompiler does not support. The tools we use are relatively conventional, including kapt and kotlinpoet. The overall idea is to get the XmlResourceParser through Resources.getLayout, and then traverse the tags in each xml through the parser's continuous next. The generated code is as follows:

When encountering merge and include, we need to specially handle the logic of recursive calls so that the parent-child layout can be connected together.

After replacing some layout implementations in the homepage with this new method, we found that the layout time of p90 in the online homepage was reduced by 200ms+, and indicators such as duration, CES, and retention were significantly improved.

The working process of LayoutInflater

The working process of LayoutInflater can be simply represented by the following figure:

The solution described in this article is to use apt to generate code during compilation. After conveniently parsing the layout file, we use the generated code to directly create an instance. Its efficiency is theoretically consistent with the efficiency after hitting the AppCompat basic component logic.

For AppCompat basic components, you can view the AppCompatViewInflater.java source code (partially shown above), which includes more than a dozen commonly used basic components such as TextView and Button.

As far as a specific layout is concerned, the performance that can be improved through the use of Layout2Code is only for other components besides the basic components, especially when the layout uses a large number of custom components, the effect is particularly obvious.

This also gives us another reminder. For example, if you write TextView / TextViewCompat in xml, the eventually created instances under the action of AppCmpatViewInflater will be TextViewCompat. But when Layout2Code or X2C-like solutions are not used, their efficiency is different. The former hits the direct creation logic in the above figure, while the latter will be created through reflection.

Disadvantages of X2C

In addition to the above optimizations, X2C also moves the reading and parsing of layout files to the compilation stage to reduce IO overhead. But the biggest difficulty in parsing xml during compilation is that we need to translate the attributes of View one by one. The reason is that there is no dependency on the SDK during compilation, so it is impossible to generate an AtrributeSet object for direct consumption by the constructor of View.

As a result, manual maintenance of translation rules is required to convert xml attributes into code for setting View attributes, which brings several problems:

1. The amount of code generated increases exponentially

2. Requires extremely high maintenance costs to support custom View properties

3. Some xml attributes do not have corresponding methods or are not in one-to-one correspondence.

All in all, it is very difficult to maintain robust and complete functionality on this basis. Compared with the new solution of Layout2Code that we are exploring, it has huge advantages in compatibility and maintenance costs. The only thing that needs to be weighed is how much optimization space there is for reading the layout file at runtime, and whether it is worth the investment.

The particularity of layout files

When it comes to xml files, you will reflexively think of IO operations with poor performance. This is true, but layout files are quite special. During the packaging process of the Andorid application, AAPT will package the resources and compress the xml files in the asset folder through string pool reuse, binary conversion, etc., and finally generate the compressed resource files and resource file index resources .arsc There are also R files. When using AssetManager to load resource files, we will also use mmap to reduce IO costs.

By analyzing the pros and cons of the above methods, we found that reading layout files usually takes no more than 1ms after testing in actual application scenarios. Therefore, considering the maintenance costs caused by moving the reading and parsing of layout files to the compilation stage, we finally chose to abandon this part of the optimization directly.

In the current development environment, the Layout2Code solution can still play a big role in improving performance. Of course, the prerequisite for effective use of this solution is that developers fully understand the principle of the solution and its specific scope of application (non-AppComapt components) ).

Compared with the traditional X2C solution, Layout2Code has a wider scope of application and lower maintenance costs. At present, this solution has been widely used in Xiaohongshu APP and has brought us good benefits and effects. Our research on Layout2Code is implemented by kotlin and using kapt. In the future, we also plan to access ksp to reduce the compilation time and continue to optimize this solution.

Xiaohongshu   Business Technology Android Engineer

Aya Ren    Xiaohongshu Business Technology Android Engineer

Guess you like

Origin blog.csdn.net/REDtech_1024/article/details/130084859