Author: Jing Jun
"Previously, we published the first article in the "Cube Technology Interpretation" series "Alipay's new generation of dynamic technology architecture and selection overview" , this is the second article in the Cube series, which provides an in-depth interpretation of the Cube card technology stack. Welcome everyone to pay attention."
Dynamic card background
Since the Windows era, application icons have become the main entrance for users (traffic), and have continued to the mobile terminal era. The disadvantage of the way the icon is the entrance is that it is not intuitive, and it takes at least one click to access the desired information. In this context, the iOS system and some Android systems have implemented cards that put content and services in front. For example, as shown in Figure 1 below, the card on the left screen of Apple carries the display of weather & stock market content. In addition, Hongmeng system also proposed a similar card scenario as a certain kind of traffic entrance. In fact, it is more common to use cards inside applications as content display and service entrances. Figures 2 and 3 are the Alipay homepage and the wealth management page of China Merchants Bank, respectively. Each small rectangle is a card. For operations, the card style and content can be configured at any time without waiting for the application version to be upgraded, which is just a matter of necessity.
Cube card summary
Cube card is a set of cross-platform dynamic card solutions developed internally by Ant Financial. It serves the regional dynamic technology in the application page, oriented to content operation, and helps product technology improve development efficiency and operational efficiency. Each Cube card is independently embedded in an area in the native page, and the content of the area is displayed through a card template. The positioning of the card is roughly as follows:
Cross-platform consistency
- A set of codes
- Effect alignment
- Dynamic interface structure & style
- Dynamic business logic
High performance
- Extreme performance
- Extreme memory
Let's expand on high performance. Cube cards pursue a close to native experience. We have defined two dimensions:
One is extreme performance. On the basis of the Cube applet capabilities, we removed some complex CSS capabilities, such as pseudo-type pseudo-elements, inline/block, etc., and also restricted the capabilities of js (Cube cards use quickjs as the script engine). In addition, we also made some optimizations to quickjs, including not limited to offline atom compilation optimization, asynchronous gc optimization, etc. We have also introduced wamr as the "coprocessor" of quickjs to support users' mixed development using javascript and assemblyscript. In this way, users can use assemblyscript some hot functions or modules.
Another dimension is the ultimate memory. In the infinite drop-down of the information waterfall scene, the memory growth of the Cube card is close to that of the Native card. We have made a finer classification of the card's capabilities, and reduced the memory consumption at runtime by configuring it during development. The following figure shows a simple card, the project directory of the Cube card as shown in the figure, and the real code and running effect of a certain card in the wallet.
Cube card production & workflow
Development period
- Local development
Cube cards are equipped with independent development tools that support card compilation, log output, real-time preview and other functions. Vue, as the dsl language of the current development template, supports js and css editing card styles.
- Card management
After the local development of the card is completed, the compiled product of the card is uploaded and released through the card management background, and the card version can be managed. After the card is released, the card can be dynamically updated on the client side.
Runtime
In order to facilitate terminal services to access Cube cards, we introduced the concept of a Cube card container (CardSDK). CardSDK encapsulates some of the functions that are closely related to the business layer/server, and common capabilities. For example, we use CubeCardSdk to pull cards and business data from the server. In addition, CardSDK is also responsible for the access of commonly used JSAPI and third-party components. In this way, Cube cards can focus more on the card product itself.
Core system architecture
The system architecture of the Cube card mainly includes JSEngine, CardEngine, RenderEngine and Platform. Most of the code is implemented in C++.
JSEngine
Mainly responsible for card js logic execution and card data change monitoring, so as to support developers to write some business logic capabilities inside the card to realize the dynamic change of card content and style.
Because the card scene has high performance requirements, comprehensive package size and performance considerations, we chose quickjs as our js basic engine library, and implemented a very small js responsive framework (JSFM) to support the card The logic code ability.
CardEngine
Mainly responsible for the analysis and binding of card data, card logic rendering, building DOM instructions, JSAPI management, JSBinding, Native event communication, etc.
In the initialization and construction process of the card DOM tree, we did not put it in the js runtime, but directly used C++ for instruction generation and tree construction in the card instance initialization link. On the one hand, it is to keep the js framework smaller and faster. On the other hand, C++ runs more efficiently.
RenderEngine
The back-end rendering base is responsible for the process of card layout calculation, style analysis, layer calculation, self-drawing components, same-layer rendering, rasterization and screen display, as well as interactive effects such as gestures and motion effects.
Platform
Platform-related interfaces, including atomic view encapsulation, Canvas API, three-party component extension protocol, animation api, etc.
Thread model and data model
Thread model
The main threads in the life cycle of a Cube card include business threads and engine threads. The business thread is the initialization phase of the card data, which is initiated and executed by the business, and is the beforeCreate phase of the card life cycle. The engine thread is the common thread of all the card life cycle running stages, mainly including the Bridge thread, the Render thread, the Paint thread and the UI main thread.
Bridge thread
The js runtime thread is also a Dom node data query and processing thread. Because based on the small and fast positioning of the Cube card, the js logic is only an auxiliary capability of the card and does not have the ability to overly complex business logic. Therefore, the Bridge thread is relatively light and designed to be Single thread mode.
Render thread
Rendering-related data calculation threads, including rendering tree construction, node-level calculation, layer layered rendering calculation, gesture data calculation, and rendering task construction. The Render process mainly involves the recursive calculation process of the tree. The rendering process takes a short time and is designed as a single Thread mode.
Paint thread
The drawing thread performs hierarchical drawing of card nodes and rasterization tasks. The Paint thread is not a fixed thread. According to the current task model, the Paint thread may be the main thread or a child thread in a thread pool; in the synchronous rendering mode, the Paint thread is directly the main thread; and in the asynchronous rendering mode Next, a thread pool is used to implement concurrent rendering of Paint tasks to improve rendering efficiency, such as in a list sliding scene.
UI main thread
The UI operation main thread is the current platform thread, which mainly includes gesture recognition, UI screen display, and data update of three-party expansion components.
In addition to the main threads mentioned above, there are also playground background threads related to burying points and monitoring, and the overall priority is relatively low. The overall threading model design minimizes the pressure on the UI main thread and improves the efficiency of concurrent rendering of cards. However, there are still some shortcomings, including frequent UI thread switching, and the bridge thread is getting heavier and heavier. We will continue to optimize the thread model in the future.
Data model
The data model corresponding to the thread model mainly includes three trees: NodeTree, RenderTree, and LayerTree. In addition to this, there is also a temporary PaintTree;
NodeTree
The original node tree of the card corresponds to the Dom tree at the front end, and the engine will do style analysis and layout calculations based on the NodeTree;
RenderTree
Rendering the data tree. This is a deformed tree. In many cases, its tree hierarchy is the same as NodeTree. In fact, when designing and defining the engine data model, we discussed whether or not this tree is necessary and whether it is necessary or not. Such a tree with the same level as the NodeTree, we still retain it, because this tree can adjust the tree relationship more flexibly. If the card is divided into the layout stage and the rendering stage, then this tree is the source tree of the rendering stage. .
Facts have proved that our decision is correct. Our subsequent support for zindex/static and other capabilities are all because the existence of this tree can be well supported at the engine layer, without the need to simulate this level of change at the platform layer. This leads to very limited scene support, including the future rendering of snapshot technology can also be considered from this tree.
LayerTree
The LayerTree tree, as the name implies, is a layered tree. The nodes are layered on the basis of RenderTree. The nodes of the same layer are rendered and rasterized in the same rendering task pipeline. Different layers are independent of each other and can be rendered concurrently.
PaintTree
PaintTree is a temporary tree. In fact, it is strictly a copy tree. It copies a subtree through RenderTree. It is generated temporarily every time rendering occurs. Of course, some node optimization processing will be done. For example, nodes that are completely covered will be optimized. Adjust to avoid repeated rendering. There is a PaintTree on each layer, and nodes are drawn through PaintTree to generate rasterization instructions or bitmaps.
High-performance list rendering
For the scenarios where cards are used in the list, the main consideration is the impact of jams, especially for low-end and mid-range devices. Cube cards support asynchronous rendering, so they can be very smooth in the list scene. At the same time, because they support multi-threaded concurrency, multiple cards can be rendered concurrently, so there will be no obvious white screen effect under asynchronous rendering conditions.
Native technology optimization
We expect cards to serve the dynamic display of regionalized content and simple business logic on the page, and are more for mobile developers. Even if the card DSL language description we use is a front-end language, we also hope to be able to restrict the use of CSS and support limited CSS capabilities, but at the same time, we also hope to cover some of the CSS capabilities commonly used by developers as much as possible.
Therefore, we did a special work on CSS capabilities, and worked with the front-end team technical students to make the Cube card CSS capabilities specification, which restricted the CSS capabilities. Even so, under the Native rendering engine, if you want to support these capabilities very well, there are many difficulties, including zindex support, overflow, etc., so we have also optimized based on some dependent platform capabilities.
Layer container
We introduce the concept of Layer container. When we introduced the data model earlier, we mentioned LayerTree. Each Layer node is an independent rendering container, and the platform View serves as a Layer container to render other virtual nodes. If according to the usual practice, a View corresponds to a rendering container, we use two Views to combine as a Layer container (iOS uses CALayer) to separate the content layer and the logic layer. This has many advantages, such as the shadow drawing of the layer node to limit clipping Issues, optimization of canvas cutting for content layer, etc.
Gesture modification
The optimization and transformation of gestures is mainly to solve the limitations of the platform system's gesture distribution capabilities. Whether it is the Android platform or the iOS platform, the system has some restrictions on the distribution of gestures, such as sibling nodes cannot distribute events (iOS), and cannot receive events beyond the parent node area. Events (affecting overflow capabilities), etc., so gestures need to be modified.
Because card rendering supports three-party component extension, in order not to affect the event response of the extended component, we take over the system gesture behavior based on the Layer container, and internally manage the gesture distribution of the container node. For scenes with mixed rendering of three-party components, the Layer container and the three-party The distribution of gestures between components maintains system behavior.
Rasterization
The Cube card rendering process includes two rendering modes: instruction rendering and bitmap rendering. These two modes will switch under different scene conditions to optimize performance in different scenes, such as frame rate and memory. Bitmap rendering is relatively complicated on Android. The default is to use Bitmap as the cache for offline rendering. The disadvantage is that it introduces an extra cpu/gpu memory copy and cannot make full use of GPU resources. The advantage is that it has good compatibility. We have tried using textureview as an offline rendering buffer, and found that devices below 6.0 have serious compatibility issues, and the stability of different devices varies greatly.
At the same time, the rasterization capability depends on the Canvas API of the platform system. Some high-level methods will involve hardware acceleration limitations, including shadow api and the system's limitation of glRender buffer (Android platform). We have also done view segmentation rendering for large canvas scenes. To ensure rendering performance. Our colleagues are also working to replace the Canvas API on the platform layer with Skia Canvas api.
Same layer rendering
The Cube card treats the tripartite component as an independent layer to update data separately, which can be very convenient and efficient to access the extended tripartite component. Based on the system's UI capabilities, the expansion components are naturally rendered uniformly in the card. At the same time, it supports the reuse of components on different cards. In actual business scenarios, the same layer rendering also brings many additional problems. Components such as maps/videos/animations are generally accompanied by large performance memory overhead. These overheads will have a negative impact on the rendering of the card, especially when the list is scrolling. For the map/video component, we cooperated with the component provider's case by case to solve the problem, and tried to set the card point when the card went online. For animation components, Cube continues to expand attribute animation/frame animation capabilities, and has built-in canvas capabilities.
Cube card business status and future planning
At present, Cube cards have served 20+ business scenarios such as the wallet's homepage, securities (stocks), card packages, and travel, with a daily pv of more than 10 billion. For a long period of time in the future, our main energy will still be focused on the business scenarios inside the wallet and cube the stock of native cards/h5 cards. To serve the scenarios in the wallet well, on the one hand, you need to do a good job of the developer experience, such as developing and debugging the tool chain, on the other hand, you must continue to optimize the basic performance, such as the pursuit of smaller package size, lower memory, and so on.
A key direction of card future planning is commercialization, that is, exporting Cube cards to small and medium-sized Internet companies and financial companies. This part of the work has been started for some time, and it is expected to be released as an extension of https://tech.antfin.com/products/MPAAS
Another direction of the card's future planning is the application development stack of IoT devices (such as RTOS). To be precise, it is not a Cube card, but an intermediate form of the Cube card and the applet. The interface of the Internet of Things device is generally simpler and similar to a card; however, it requires routing capabilities between multiple "cards", which is closer to the application form. Such a hybrid form can not only retain the advantages of Cube card in memory/performance/package size, but also meet the demands of IoT device application development. According to our research, most of the RTOS application development environment still stays in the traditional C language, and the performance and dynamics are not ideal. For developers, Cube may be an option.
Trailer
If you are interested in this series of articles, thank you for your continued attention to this official account [Alibaba Mobile Technology], and we will continue to talk about the next Cube technology interpretation article.
, 3 mobile dry goods & practice for you to think about every week!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。