With the booming development of mobile smart devices, a mobile multi-terminal development framework has become a general trend. Download the Flutter Analysis and Practice: Evolution and Innovation of Xianyu Technologies eBook for step-by-step analyses based on real-life problems, as well as clear explanations of the important concepts of Flutter.
Apps of a certain scale usually have a set of mature and universal fundamental libraries, especially for apps in the Alibaba system, which generally depend on many fundamental libraries in the system. The cost and risk of using Flutter to re-develop an app from scratch are high. Therefore, incremental migration in native apps is a robust way for Flutter technology to be applied in existing native apps. The tech team from Xianyu, Alibaba's second-hand trading platform, has developed a unique hybrid technology solution in this practice. During the process, Xianyu has closely communicated with the Google Flutter team, and selected the solutions and implementation methods based on the Google Flutter team's suggestions and actual business situation.
The Flutter technology chain is mainly composed of the Flutter engine written in C++ and the Flutter framework written in Dart. The Flutter engine is responsible for thread management, Dart VM status management, and Dart code loading. The Flutter framework is the main API that exposes services externally. Concepts, such as widgets, are the Flutter framework content at the Dart level.
Only one Dart VM can be initialized in a process. However, a process can involve multiple Flutter engines, and multiple Flutter engines share the same Dart VM.
Let's look at the specific implementation. Every time a
FlutterViewController is initialized for an iOS project, an engine will be initialized, which means that a new thread (theoretically, the thread can be reused) will run the Dart code. Similar effects can be achieved for activities on Android. If multiple Flutter engines are started, the Dart VM is still shared, but the code loaded by different Flutter engines runs in their separate isolates.
1) Deep Engine Sharing
The recommendation from Google for Flutter is that, in the long run, the ability to support multi-window rendering in the same engine should be supported. At least logically, FlutterViewController shares the resources of the same engine. In other words, we want all the rendering windows to share the same primary isolate.
However, Google's official long-term recommendation is not well supported.
2) Multi-Engine Mode
In the hybrid solution, the main problem to be solved is how to deal with the alternate Flutter and native pages. Google engineers provide a Keep It Simple solution: Display continuous Flutter pages (widgets) only in the current
FlutterViewController. For alternate Flutter pages, initialize a new engine.
For example, let's perform the following navigation operations:
Flutter Page1 -> Flutter Page2 -> Native Page1 -> Flutter Page3
We only need to initialize a Flutter engine for Flutter Page1 and initialize another one for Flutter Page3.
This solution is easy to understand. However, if a native page and a Flutter page alternate all the time, the number of Flutter engines increases linearly, but the Flutter engine itself is a complex object.
3) Problems of Multi-Engine Mode
Therefore, Xianyu has not adopted the multi-engine hybrid solution in comprehensive consideration.
The hybrid solution currently used by Xianyu is to share the same engine. This solution is based on the fact that only one page at most can be seen at any time. However, in specific scenarios, several ViewControllers can be seen, which will not be discussed here.
We can simply understand this solution in this way: Consider the shared Flutter View as a canvas, and then use a native container as the logic page. Every time we open a container, we use the communication mechanism to instruct the Flutter View to render the current logic page, and then put the Flutter View into the current container.
The old solution maintains a Navigator stack at the Dart layer, as shown in Figure 1-20. This solution cannot support multiple horizontal logic pages at the same time, because you must perform operations from the top of the stack during page switching, and cannot perform horizontal switching while maintaining the status.
For example, for two pages A and B, page B is currently at the top of the stack. To switch to page A, you need to pop page B out from the top of the stack. At this time, the status of page B is lost. If you want to switch back to page B, you can only re-open page B. The earlier status of the page cannot be maintained. This is the biggest limitation of the old solution.
During the pop process, the official Flutter Dialog may be mistakenly killed.
In addition, stack-based operations rely on attribute modifications to the Flutter framework, which makes this solution invasive.
When Xianyu promotes Flutter, more complex page scenarios have gradually exposed the limitations and problems of the old solution. Therefore, Xianyu has launched a new hybrid technology solution codenamed FlutterBoost. The main objectives of the new hybrid solution are:
Similar to the old solution, the new solution still adopts the shared engine mode. The main idea is that a native container drives a Flutter page container by using messages, which achieves the synchronization between the native container and the Flutter container. We want the content rendered by Flutter to be driven by the native container.
Simply put, we want to make the Flutter container into a browser. After a page address is entered, the container manages page rendering. On the native side, developers only need to consider how to initialize the container, and then set the corresponding page tag of the container.
1) Native Layer
2) Dart Layer
3) Understanding of Pages
The object and concept of the page expressed in native and Flutter are inconsistent. In native, a page is generally expressed as a
ViewController and an Activity. However, in Flutter, a page is expressed as a widget. We want to unify the concept of pages or abstract the concept of pages corresponding to the widgets in Flutter. In other words, when a native page container exists,
FlutterBoost ensures that a widget is used in the container. Therefore, the native container should prevail for when we understand and perform routing operations. Flutter widgets depend on the status of the native page container.
Then, when we are talking about pages in the
FlutterBoost, we refer to the native container and its affiliated widgets. All page routing operations, including opening or closing pages, are performed in native page containers. No matter where the routing request comes from, it will eventually be forwarded to native page containers to implement the routing operation. This is why the platform protocol needs to be implemented when
FlutterBoost is accessed.
In addition, we cannot control the business code to push new widgets through Navigator of Flutter. If a business uses Navigator to operate widgets without using
FlutterBoost, we recommend that the business manage its status. This type of widget does not belong to pages defined by
The page concepts are critical for understanding and using
The old solution maintains a single Navigator stack structure at the Dart layer for widget switching. The new solution introduces the container concept at the Dart layer. Instead of using the stack structure to maintain existing pages, all current pages are maintained in the form of flat key-value mapping, and each page has a unique ID. This structure naturally supports page searching and switching and is no longer subject to operations on the top of the stack. Therefore, some problems caused by pop can be solved. Moreover, the page stack operation does not need to be performed by modifying the Flutter source code, eliminating the intrusiveness of the implementation.
In fact, the container we introduced is the Navigator, that is, a native container corresponds to a Navigator. How does this work?
Flutter provides an interface for developers to customize Navigators at the underlying layer and we have implemented an object for managing multiple Navigators. Currently, only one visible Flutter Navigator is available at most. The pages contained in this Navigator are the pages corresponding to the visible container.
Native containers map Flutter containers (Navigators) one by one, and their lifecycles are also synchronized. When a native container is created, a Flutter container is also created, and they are associated by the same ID. When the native container is destroyed, the Flutter container is also destroyed. The status of a Flutter container varies with a native container, which is native-driven. Manager centrally manages and switches the containers displayed on the screen.
Let's use a simple example to describe the process of creating a new page:
This is the main logic for creating a page. Operations such as destroying and entering the backend are also driven by native container events.
Currently, FlutterBoost has been supporting all Flutter-based development services on the Xianyu client in the production environment, providing support for more complex hybrid scenarios, and stably providing services for hundreds of millions of users.
At the very beginning of the project, Xianyu wanted FlutterBoost to solve the general problem of connecting a native app to Flutter in hybrid mode. Therefore, FlutterBoost is made as a reusable Flutter plug-in, expecting to attract more users to participate in the construction of the Flutter community. Xianyu's solution may not be the best. However, Xianyu hopes that you provide more excellent components and solutions in the community.
When switching between two Flutter pages, we have only one Flutter View, so we need to save a screenshot of the previous page. If there are many Flutter pages, the screenshots occupy a large amount of memory. Therefore, the L2 cache policy of file memory is used, in which only two or three screenshots can be saved. Other screenshots will be loaded as needed when written to files. In this way, we can maintain stable memory usage while ensuring the user experience.
In terms of page rendering performance, the advantages of Flutter ahead-of-time (AOT) are obvious. During fast page switching, Flutter can switch pages flexibly, logically creating a sense of Flutter with multiple pages.
At the beginning of the project, we developed based on the Flutter version currently used by Xianyu, and conducted the Release 1.0 compatibility upgrade test. No problems have been found so far.
Any project integrated with Flutter can easily introduce FlutterBoost as a plug-in in an officially dependent way and only a small amount of code access is required for the project. For more information about access documentation, see the official project documentation on the GitHub homepage.
Flutter Analysis and Practice: Quick Building of Hybrid Projects
Flutter Analysis and Practice: Native Capability-Based Plug-In Extension
56 posts | 3 followersFollow
XianYu Tech - September 2, 2020
XianYu Tech - September 9, 2020
XianYu Tech - September 11, 2020
XianYu Tech - August 10, 2021
XianYu Tech - September 11, 2020
XianYu Tech - September 7, 2020
56 posts | 3 followersFollow
Help enterprises build high-quality, stable mobile appsLearn More
An enterprise-level continuous delivery tool.Learn More
Provides comprehensive quality assurance for the release of your apps.Learn More
Accelerate software development and delivery by integrating DevOps with the cloudLearn More
More Posts by XianYu Tech