头图

"Dutter Series Articles" will describe the technical practice and experience of stepping on the four-terminal application framework (codenamed Dutter) built by DingTalk based on Flutter. The next article "Lessons from the past: Talk about the "pit" that Dingding Flutter stepped on the desktop side", welcome to follow up & read.

Author: Liu Taiju (Nuo Liang)

This article mainly introduces DingTalk's four-terminal application framework (code-named Dutter) based on Flutter. The content mainly includes solution design, best practices, and problem location at the FlutterEngine level. I hope that the sharing of this article can provide some reference for teams with similar demands.

Project Overview

1.1 What is Dutter

Dutter is DingTalk Flutter, which is a four-terminal R&D framework based on Flutter in DingTalk.

The Dutter project "begins with Flutter but doesn't stop at Flutter". The main goal of the project is to use Flutter's cross-platform capabilities to improve the end-side R&D efficiency of DingTalk without reducing the user experience, and alleviate the problems of insufficient DingTalk end-side R&D resources and unbalanced manpower at each end.

1.2 Current progress

At present, the Dutter running framework has completed the four-terminal integration of DingTalk, and has completed a series of grayscale and pilot projects for co-creation of business. At this stage, the Flutter-based R&D business in DingTalk includes "schedule check-in", "+ panel" and some internal grayscale businesses:

Background of the project

We chose Flutter and started the Dutter project for two main considerations:

  1. End-to-side R&D to improve efficiency;
  2. Follow up on Flutter technology.

Below we briefly explain this.

2.1 End-to-end R&D to improve efficiency

As DingTalk reaches its 7th year, the contradiction between the client-side "business requirements", "R&D resources" and "technological evolution" has become more and more intense:

  • Business product students have many excellent ideas. In order to implement the ideas, they need to find TLs at various ends to strive for resources, and because of the shortage of R&D resources, they need to repeatedly communicate the business value of their needs;
  • R&D students are often in a state of "1 vs N", with business needs, stability assurance, technical support, BugFix, etc., their daily working hours are basically saturated;
  • The technical team should not only be satisfied with the present, but also face the future. While meeting the daily business iteration, we also need to arrange some resources to invest in technical projects that meet the development of the next 3 to 5 years.

The above points can be summed up into one problem: our technology research and development resources are insufficient. To solve the above problems, there are two ways: 1. Continue to expand the scale of the technical team; 2. Improve the efficiency of team R&D.

Judging from the current team size of nearly 150 people on the DingTalk side, the overall volume is not small, and it is difficult to continue to expand the recruitment. Since the size of the team cannot be expanded indefinitely, we need to explore room for improvement in R&D efficiency:

  • The end-to-end technical students are divided into 5 platforms, and the manpower on each platform is not sufficient after the division;
  • Students on different platforms are basically in an "isolated" state, and students on different platforms cannot complement each other;
  • Any business requirement requires more than 4+ end R&D resources investment, and the lack of manpower at any end may cause it to fail to land;
  • It is difficult to achieve complete consistency by implementing multiple copies of one logic. There are often inconsistent business performances on different platforms, and rework focusing further affects efficiency;
  • After the business is launched, different platforms are maintained separately, and multiple investments are required in scenarios such as daily technical support and BugFix.

It can be seen that if we can use cross-platform technology to enable technical students to cover all terminals through "one code implementation", the original requirements that require multiple platforms and multiple students to do are converged to 1~2. Students, we can greatly improve our research and development efficiency.

2.2 Follow up on Flutter technology

DingTalk already has cross-end technologies such as "Mini Programs" and "H5". Can we directly use the existing technology stack to achieve our goals if we need to improve efficiency? For the DingTalk end-side team, it is theoretically feasible to choose a web-based solution for cross-platform, but it is difficult to achieve the expected effect in practice. The main reasons lie in two aspects:

  1. "Small program technology" is currently a relatively popular cross-end technology. Its design orientation should meet the three-party ecological diversity scenarios, and its architecture design focuses on "big and complete" rather than repeated polishing on a single point. This is different from the "specialized and refined" and "pursuit of the ultimate" emphasized by Dingding's business;
  2. For end-to-end students, the front-end development model has a high threshold for getting started, and the R&D model is quite different. It is necessary to have a certain amount of use and accumulation of development experience in order to have a high development level, which means that there is a certain "trial and error space" in the early stage. With Dingding's current requirements for online quality, this is also difficult to meet.

As a cross-platform technology developed in recent years, Flutter is different from the Web ecosystem. Its native-like architecture design selectively abandons dynamics and focuses more on cross-platform. On the basis of ensuring similar Native performance and experience, it gives developers the ability to "build and run multiple terminals at once". Therefore, compared to the applet technology, Flutter is more suitable for solving the pain points of our end-side technical team.

In addition, after conducting a thorough investigation of domestic cross-platform technology, we found that the cross-platform project based on Flutter has obvious advantages, high ceiling, great development potential, and more long-term investment value.

In the long-term tracking of the industry's cross-platform solutions, we found that the "self-drawing engine" is a hot topic at this stage, and most of the "self-drawing engine" solutions started after the Flutter project was open sourced and became popular. The coincidence at this point in time is not accidental. We use the following diagram to illustrate the technical differences between mainstream cross-platform solutions:

From the above picture, we can see that for the designers of cross-platform solutions, the biggest value of the Flutter project is: to provide an open source, excellent design, excellent compatibility, excellent performance and clear boundaries for the ecology Self-drawing engine.

Based on this open source self-drawing engine, teams with skills can apply it to their own cross-platform solutions with minor modifications to replace Native components, reuse the cross-platform consistency capabilities of Flutter, and improve solution business. and technical value.

For DingTalk, considering our cross-platform investment and goals at this stage, we have not launched our own cross-platform self-drawing engine like other solutions. However, from the perspective of technology, choosing to use Flutter as a cross-platform solution, on the one hand, we can quickly enjoy the technical dividends of Flutter, and the performance and quality of delivered products are consistent with other mainstream solutions; on the other hand, we can also be in this process. Cultivate relevant technical teams in China to make technical reserves for subsequent deeper customization and transformation.

Design

This chapter will give an overview of the design of DingTalk Dutter's cross-end framework, and make some supplementary explanations for representative issues.

3.1 Overall Design

The Dutter core module consists of three major suites:

  1. Dutter Runtime;
  2. Dutter Dev Kit;
  3. Dutter OPS Kit.

The overall picture is as follows:

  • Dutter Runtime: The Dutter runtime environment built on Flutter is the core part of Dutter. In addition to the basic functions provided by Flutter, we also provide functions such as containerized components, API plug-ins, and business modular frameworks. And on the basis of the group's AliFlutter project, Aion's dynamic functions have been further expanded. Dutter Runtime is also a part of our project that we are fully committed to now;
  • Dutter Dev Kit: The research and development kit, the main purpose is to solve the support and efficiency problems of students with different technology stacks when developing across 4+ terminals. The current investment is relatively limited, and it can be integrated with the DingTalk R&D platform in the future;
  • Dutter OPS Kit: The operation and maintenance kit, which mainly carries Dutter product release and operation and maintenance related functions, such as large-scale monitoring and so on. The current investment is relatively limited, and it can be integrated with the DingTalk R&D platform in the future.

Expand the above diagram to get the overall module diagram of the framework, which is roughly as follows:

From bottom to top this is:

  • The lower left corner is the "Dutter Runtime" related modules;
  • The lower right corner is the "Dutter OPS Kit" related modules;
  • The upper right corner is the "Dutter Dev Kit" related modules;
  • The upper left corner is the business section.

3.2 Data Communication

Data communication mainly refers to the two main communication methods between Flutter and the platform side: Channel and FFI. Channel is relatively widely used in Flutter applications. Most of the design and communication between Flutter and the platform are based on this mode. Its advantages lie in high integration, good packaging and easy use; the disadvantage is mainly in communication efficiency; FFI has been implemented in Flutter 2.0 Launched as a formal feature, its biggest features are synchronous calls, memory sharing, and high execution efficiency, but there is still room for improvement in terms of ease of use and scalability.

Channel

Regarding Channels, there is no essential difference between the use of DingTalk and the official documents. The experience I want to share lies in the management of the number of Channels. The official native data does not involve much content related to Channel management. Based on the actual use experience of DingTalk, we still recommend that you try to converge Channels to 1~2 for sharing in one business, and share on the basis of Chennel. Encapsulates the "response" and "distribution" interfaces for business use.

The main benefits of doing so are:

  1. Conducive to performance stability, limited Channel can reduce the probability of abnormal communication and improve communication performance;
  2. It is beneficial to management, especially in the "single-engine/multi-engine" coexistence mode, and the underlying differences can be smoothed out through reasonable encapsulation.

The above two points, especially the second point, are of great significance for Dingding to be compatible with the mobile terminal and the desktop terminal. It is explained in "DingTalk Flutter Desktop Application Solution" that we are using a single-engine architecture on the mobile side, but a multi-engine architecture on the desktop side. If there is no reasonable encapsulation of Channel, and business students can directly register and call FlutterEngine, it will greatly increase the cost of code management in the multi-engine mode, and cause inconsistency between mobile and desktop implementations.

Our current approach is to encapsulate the FlutterEngine and Channel into the Dutter framework, and expose the unified encapsulated instance to the upper interface: DutterMethodChannel. For the business layer code, there is no need to perceive whether the underlying architecture is single-engine mode or multi-engine mode, and only need to register or call related services according to unified rules and modes. Through this mode, while reducing the complexity of business use, it also brings great flexibility to the design of the underlying framework, and provides strong support for the subsequent mobile terminal switching multi-engine solutions.

FFI

FFI has been officially released in Flutter 2.0. Its biggest advantage over Channel is that it has higher execution efficiency and is more suitable for scenarios with higher performance requirements. This chapter does not involve the use of specific FFI, but wants to briefly share the matters needing attention in memory management when using FFI.

We all know that the current mobile development (Java, OC, Swift) has a mechanism to automatically manage memory; the dart language used by Flutter also has automatic memory management based on garbage collection. Various languages can manage memory reasonably according to their own rules in their own scopes to ensure a reasonable and stable application of memory space.

However, as a method of direct adjustment across functions, although FFI simplifies the call chain based on the memory sharing mechanism, it also puts forward higher requirements for memory management. In this mode, if the memory space cannot be managed (opened up & released) well, there is a high probability of causing wild pointers or memory leaks.

In the introduction of the official documents Flutter FFI and Dart FFI chapters, the description of memory management is relatively limited. By consulting the relevant interface information, we can see that the manual memory management method is provided in dart:ffi:

On this basis, we can define the Dutter FFI memory management strategy. First we need we need to define the core principles precisely:

  1. Allocation and release of the same source: a set of alloc and free algorithms must be used to avoid memory allocation and release exceptions due to implementation differences;
  2. The principle of "who alloc is free" must be met.

On the basis of 1 and 2, we encapsulate the FFI operation-related interfaces and data structures and unify them into the "Dutter FFI Bridge" module.

After full consideration of coverage and complexity, we only add support for String type in addition to the default basic type in the Dutter FFI interface. For other data types, business parties can transfer them by serializing them. During the transmission process, fixed-length strings can be passed directly through "UTF-8 encoded char * array"; if it is a variable-length string (such as the return value of a call), it needs to be passed using a custom data structure DTFUInt8String. Specific to implementation:

1. In order to meet the principle of "same origin for allocation and release", in Dutter, we choose the allocate and free methods in dart:ffi as the implementation of unified allocation and release. The Dutter framework will do an interface binding during the startup process, and pass our custom data structure related methods to the Native side. All FFI interface memory allocation scenarios on the Native side are implemented through the binding interface:

2. In order to meet the principle of "who alloc is free", in the Dutter FFI interface, we agree on the following three principles by default. On this basis, it can be ensured that the allocation of heap memory is within the control range of DTFUInt8String. As long as the life cycle of the DTFUInt8String object is well handled, the security of memory management during the transfer process can be guaranteed:

a. When designing the interface, use DTFUInt8String to transfer data for scenarios that require variable-length return values;

b. In order to improve the transfer efficiency, try to transfer DTFUInt8String as a pointer;

c. The caller is responsible for creating and releasing DTFUInt8String.

3.3 Message bus

"Message Bus" is a DingTalk feature module. We mainly solve the business communication problem implemented by DingTalk based on different technology stacks: for example, a business based on Flutter, we hope to notify a page refresh UI based on applet. , you can achieve this function through the message bus:

The message bus positioning is a lightweight "end" to "end" super channel, the goal is to enable the business to have the ability to communicate seamlessly across the operating environment. In terms of logic, it includes three major modules: "bus", "controller", and "registration and sending"; in terms of implementation, the overall operation is reliable, efficient and safe through "persistent messages", "pipeline classification" and "authority control".

3.4 Modularity

Because of Dingding's end-to-end business characteristics, we attach great importance to modular construction. The modular solution adopted by the Flutter business is developed from the DingTalk Native side modular framework. We insisted on eliminating the direct coupling of the Flutter business layer from the beginning:

Modularization not only improves our R&D efficiency, but also brings significant business and technical value. for example:

  1. Provides strong support for multiple versions of DingTalk, and meets the demands of sharing code between multiple versions such as “Standard Ding”, “Big Customer Ding” and “Exclusive Ding”;
  2. Provides good compatibility, and satisfies the requirements of the same architecture of the Dutter framework on mobile and desktop through flexible plugging and unplugging of basic modules;
  3. Provides rich extensibility. For example, when we try to make Flutter dynamic, based on modularity, we can dynamically transform existing modules at a lower cost without affecting the stability of other modules.

3.5 Containerization

Containerization is a powerful guarantee to support the rapid landing of Flutter in DingTalk. Through the container foundation deposited by DingTalk in H5 and Mini Program projects, in the Flutter scenario, we continue to refer to the idea of containerization to quickly connect in design and capabilities. On the one hand, it can quickly reuse the existing infrastructure; on the other hand, it reduces the complexity of business development, ensuring that the common capabilities of the original container can continue to be used in Flutter scenarios, and the technology stack can continue.

From the perspective of the development timeline, the DingTalk end-side container has roughly gone through 3 versions:

  • The v1.0 version mainly solves the "presence or absence" problem and defines the core concepts related to containers;
  • The v2.0 version abstracts the concept of "capability package" on the original basis to ensure that basic business capabilities can be reused across operating environments;
  • The v3.0 version further abstracts the "runtime" and "extension" on the basis of v2.0, and the lower layer of the core implementation is the "container base", and the three are weakly coupled.

Based on the current container architecture, we can ensure good compatibility with future new technologies. In the follow-up development, if you need to connect to a new technology stack similar to Flutter again, you can quickly open up according to the existing standards, and ensure maximum reuse in terms of concepts, capabilities, and infrastructure.

3.6 Component library

There are two sets of component libraries currently used by DingTalk Flutter: dingui_flutter and dingtalk_uikit, of which dingui_flutter is the key part of our current construction, and dingui_flutter is a set of Flutter version components implemented according to the DingUI visual specification proposed by the DingTalk visual team. The core components are currently Four-terminal compatibility can be achieved:

The goal of dingui_flutter is to contribute to the community, but at this stage, due to stability, completeness and other issues, it is still being used internally by DingTalk for the time being. We will open source it as soon as possible after the subsequent development is mature.

Flutter desktop

At present, the usage mode of Flutter in DingTalk desktop is basically the same as that on mobile: Flutter is a functional module in DingTalk, and the main client side is still mainly implemented by the original Native. For some businesses implemented based on Flutter, the transition is performed through the interface encapsulated by the Dutter framework at startup, and the transition action is performed according to a specific transition mode.

In order to achieve the above effects, we mainly solve the following three problems in the desktop application:

  1. Desktop integration mode problem;
  2. Widows 32-bit issues;
  3. Engine architecture compatibility issues.

Later, we will explain the above problems separately.

4.1 Problems with desktop integration mode

Flutter currently only supports the use of the FlutterApp mode on the desktop side, and the FlutterModule mode widely used on the mobile side is not yet supported. However, it is neither reasonable nor realistic to expect a large-scale transformation of the existing client through FlutterApp. Therefore, the first problem we encountered when landing Flutter on the desktop was how to integrate Flutter as a module into DingTalk's existing client.

When we analyzed the Flutter build products, we found that, in fact, whether it is FlutterApp or FlutterModule, the core products are not very different. Take FlutterModule on iOS and FlutterApp on macOS as an example, as shown in the following figure:

We can see that for the core modules of App.framework, Flutter.framework, Plugins.framework, whether it is FlutterApp or FlutterModule, its products are all included. The main difference is that there is an additional FlutterPluginRegistrant.framework in FlutterModule for auxiliary plugin registration. Fortunately, this part of the implementation is not complicated, and we can easily generate it by customizing the toolchain.

Along this line of thought, we can sort out the Flutter desktop integration solution:

Use FlutterApp to organize Flutter-related modules on the desktop, and make appropriate extensions on the basis of the official toolchain. Extract the parts required for modular use from the original build products, and finally complete the template code required for plugin registration. After the final product is integrated into the existing client of DingTalk, there is no essential difference in use with other second-party libraries. You can refer to the existing FlutterModule method for use.

The final process is as follows:

Schematic diagram of product integration between Mac and Windows:

4.2 Widows 32-bit problem

Flutter does not support Windows 32-bit systems, which should be one of the core obstacles hindering the development of Flutter's domestic desktop ecosystem at this stage. When Dingding solved this problem, we basically tried all the solutions we could think of: from the initial dual process, to the overall upgrade to 64-bit in the middle, and the latter FFW, but the above solutions were ultimately due to various The problem doesn't come to fruition.

Although it failed to land in the end, in the process of the above attempt, we learned two very important information:

  1. DartVM can run on Windows 32-bit devices, but only supports loading dart code in JIT mode;
  2. Skia can compile Windows 32-bit products.

With the support of the above two points, Dingding Zhou Yong finally explored the solution of compiling Windows 32-bit FlutterEngine, and loaded the Flutter compiled product through JIT mode, which finally met the requirements for use on Windows.

In order to be able to use Flutter on the Windows platform, after stripping the details, we roughly did the following (there will be articles to share later in the details):

  1. Modify the build script of FlutterEngine so that it can build 32-bit flutter_windows.dll;
  2. Modify the FlutterPlugin compile gn parameter in flutter_tool to build a 32-bit production inspection product;
  3. After security obfuscation of related products, they are integrated into the DingTalk client.

Through the above steps, we have completed the main work of integrating Flutter in Windows 32-bit DingTalk. After that, there is no essential difference in function between JIT and AOT, but the difference in performance is large. At present, the main problems found in our grayscale process are:

  1. Slow startup: the loading time of the home page is more than 2s;
  2. Temporarily high memory usage: Each time a FlutterEngine object is opened, it will consume about 70MB of memory;
  3. Low code running efficiency: Although this problem is not obvious in most scenarios, performance problems still occur in extreme scenarios.

Therefore, what we have adopted at this stage can only be counted as a publication plan, and we still need to increase investment in this part in the future, and strive to integrate a complete Flutter into DingTalk Windows as soon as possible.

4.3 Engine architecture compatibility issues

This is the third problem we encountered in the process of landing on the desktop. Since we use a single-engine architecture based on FlutterBoost on the mobile side, the desktop side can only use a multi-engine architecture because of its special environment:

Therefore, it brings some problems to business students, the most serious of which is the communication blockage caused by the multi-engine environment.

At this stage, we mainly bypass the business layer compatibility: we use DingTalk "message bus" to support communication problems in a multi-engine environment. But in the long run, we still need to have friendly support for multiple engines. We need to extend the LightWeightEngine capabilities currently available on the mobile side to the desktop side, and expand on this basis, and open up isolate to allow business code to completely share memory. At present, this plan is being promoted as a technical project within the AliFlutter project team, and we look forward to reaching the set goals as soon as possible!

Summarize

At present, the Dutter project has basically reached its first-stage goal, and we will continue to invest in the following five areas:

  1. Infrastructure upgrade: mobile FlutterEngine upgrade, flutter_boost upgrade, exploration of dynamic solutions, etc.;
  2. Improve performance experience: Improve desktop performance, maximize the resolution of performance problems caused by current official support, infrastructure completeness, desktop features, etc., and strive to align with the mobile terminal level;
  3. Complete R&D suite: Provide a one-stop R&D environment for DingTalk. Currently, we hope to be able to expand the part directionally to meet the application development demands of DingTalk based on AliBox, facing DingTalk’s four-terminal R&D scenario;
  4. Enhanced stability: Solve the current risks on the desktop side, especially the Windows side stability, and meet the stability requirements of the DingTalk side;
  5. R&D efficiency improvement: Expand business coverage, release cross-end Manulife, and further improve Dingding's end-to-end R&D efficiency.

The above is some sharing of DingTalk Flutter's four-terminal framework on application design, hoping to bring you some help.

Pay attention to [Alibaba Mobile Technology], Ali's cutting-edge mobile dry goods & practice will give you thoughts!


阿里巴巴终端技术
336 声望1.3k 粉丝

阿里巴巴移动&终端技术官方账号。