有幾種方法可以將 C 程式碼編譯為 Wasm,但在這種情況下,我們使用了 wasienv。完整的詳細資訊可在 README 中找到。
對於此範例,我們將嘗試調用這些 Brotli 函數來壓縮和解壓縮資料:
1 2 3 4
intBrotliEncoderCompress( int quality, int lgwin, int mode, size_t input_size, constuint8_t* input_buffer, size_t* output_size, uint8_t* output_buffer);
var brotliPath = Platform.script.resolve('libbrotli.wasm'); var moduleData = File(brotliPath.path).readAsBytesSync(); var module = WasmModule(moduleData);
var memory = instance.memory; var compress = instance.lookupFunction("BrotliEncoderCompress"); var decompress = instance.lookupFunction("BrotliDecoderDecompress");
Note: This article was originally written in Chinese by the ByteDance team and translated into English.
Flutter, a technology that ByteDance has been utilizing and contributing to for several years now, was recently highlighted on the main stage of Google I/O. Developed and open-sourced by Google, the multi-platform framework for front-end UI development has garnered over 120,000 stars on GitHub.
At Google I/O, Zoey Fan, one of Flutter's product managers, talked about how the framework was adopted at ByteDance.More than 70 apps by ByteDance use Flutter as their multi-platform solution.Flutter has reduced development time by 33%, as compared to developing separate apps for Android and iOS.
Today, there are over 500 Flutter developers at ByteDance, with over 200 actively developing with the framework. These developers utilize Flutter not only for mobile apps but also experiment with it on web, desktop, and embedded platforms.
Beyond this, ByteDance has conducted fundamental work throughout the organization and made significant contributions to the Flutter project by submitting dozens of pull requests (PRs).
How did ByteDance make Flutter truly work for them?
The story of Flutter at ByteDance began two years ago.
At the time, the ByteDance front-end engineering team noticed that many teams within the company needed to develop for multiple platforms, but lacked a tool to achieve high-efficiency, high-performance, multi-platform development.
When Google open-sourced Flutter, the ByteDance team discovered that with Flutter, they only needed to develop the app once to support platforms such as Android, iOS, and web. Also, because Flutter has its own rendering engine, they could achieve more consistent performance across platforms.
With Flutter, the Android, iOS, and web versions of an app automatically stay in sync. There is no need to design and program the UI separately for each platform, eliminating a significant portion of redundant work.
To support business development more efficiently, the ByteDance team performed fundamental work on the framework itself, such as optimizing performance, creating app frameworks, containerizing, and supporting “add to app.” They also improved Flutter performance tools, including improvements to the Frames Per Second (FPS) info in the Frame chart and the timeline events chart. Both of these charts are part of the Performance View in Flutter DevTools.
When adopting Flutter, the ByteDance team encountered some unique challenges. For example, Flutter must be added to the app installation package, increasing the size of the app downloaded by users. Additionally, Flutter uses the Dart programming language, which is larger in size than native code, further increasing the package size.
The ByteDance team started a special plan to optimize the package size by compressing the iOS data section and stripping out the Skia library and other libraries (such as BoringSSL, ICU, text rendering, and libwebp). They analyzed Flutter Dart code against iOS native code and found that to implement the same business feature, the Dart code generated more machine code instructions. To close the gap, they reduced alignment of instructions, removed debugging trap instructions, dropped redundant initialization of stores with null, removed RawInstruction headers with bare instructions mode, compressed StackMaps, removed CodeSourceMap, and so on.
Individually, each of these optimizations reduced the package size by 0.2 to 4 MB and significantly reduced the total package size when combined. The ByteDance team shared their experience with Google engineers, and many improvements made their way to the Flutter open source project for the benefit of the larger community.
However, when ByteDance released their first Flutter app, new issues emerged. Users asked: ‘Why is the UI so janky when I scroll in the app?’
When the ByteDance team looked into the issue, they saw that when a FlutterView extended a TextureView, the UI was noticeably jankier than when it extended SurfaceView. However, in the official Timeline tool, the UI thread time and GPU thread time for each rendered frame are about the same, with TextureView pulling a bit ahead occasionally.
The metrics contradicted the real-world user experience, which puzzled the team.
At first, the team used the Timeline tool to troubleshoot the issue, to no avail. After digging into the tool’s source code, they discovered the root cause of the issue.
SurfaceView had better performance than TextureView. Because SurfaceView had its own surface, and rendering was performed in its own OpenGL context, it could interact with SurfaceFlinger independently and took full advantage of triple-buffering. On the other hand, TextureView was a regular view that depended on the surface of its host window for rendering. That meant the rendering wasn’t performed immediately after the UI and GPU threads had finished their work but needed to wait for the native main thread and renderThread before the view could interact with SurfaceFlinger. That was a much longer rendering pipeline than that of SurfaceView.
These findings not only helped the team eliminate the jank but led to 10 PRs being submitted to the Flutter open source project. With this fundamental work done, Flutter eventually became the go-to framework for multi-platform app development at ByteDance. Soon, the ByteDance team’s work with Flutter will be available to external developers using their mobile development framework, veMARS, benefiting the entire developer community.
From experiment to production: How ByteDance put Flutter into use
It wasn’t exactly smooth sailing for ByteDance to put Flutter into real-world use.
At first, the ByteDance team chose a mature product and planned to re-implement the app’s video playback feature with Flutter.
The feature, originally written in native code for Android and iOS, wasn’t straightforward to rewrite with Flutter. After six months, the team came to the conclusion that it would be difficult to make all the live data compatible and challenging to update the existing business logic.
The team decided that it wasn’t productive to update the existing features of a mature product with the new framework. Flutter’s strengths would be better used in a brand-new app. The team lead said, “In a mature product, everything is already well built with native Android or iOS technology. There isn’t much gain in re-implementing the features with Flutter only to make minor improvements. It also increases the package size since the Flutter engine is included in the package. In new products or new scenarios, however, Flutter can greatly increase our productivity.”
With this changed mindset, the team turned their focus to new business areas such as education.
One of their education apps in China helps students learn the order of strokes of Chinese characters; the team wanted to add a stroke tracking feature.
To implement it, the team took inspiration from some open-source projects and decided to use SVG paths to represent strokes. The paths would then be adjusted and positioned to compose the characters:
They defined the skeleton of each stroke to guide the movement of the virtual brush pen, so the pen moves just like it would in calligraphy:
Based on the defined order of the skeletons, a circle with a certain radius is drawn along each skeleton, and together these circles form the stroke. After that, the team added keyframes to ensure that the frame rate of the animation is high enough to avoid jank.
That is how they created the smooth tracking effect, as shown in the following GIF:
The feature, built with Flutter, now supports over 9,000 Chinese characters, including most of the commonly used characters. Compared to developing with native code, Flutter saved time and resources.
Today, many apps by ByteDance employ a hybrid approach to development, combining the strengths of Flutter and other technologies, with newer apps leaning towards pure Flutter. For apps such as Xigua Video, TikTok Volcano, and Open Language, Flutter increased the productivity of the teams by about 33%.
ByteDancers Embrace the Latest Technology
Even now, the Flutter team at ByteDance continues to explore the latest technologies. According to the team lead, “We have in our team many tech enthusiasts with global vision, and will continue to explore global technology developments and discuss the implementation of technology. We have close connections and collaborations with many tech companies. We have quarterly sync meetings with Google, for instance, to exchange progress, thoughts, needs, and ideas from both sides.”
One day, the maintainer of the Dart open source project on GitHub came to the ByteDance team lead with the following remarks, “Someone from your team submitted more than a dozen PRs to Dart and they’re all very good and well thought out.”
The Dart open source project maintainer was talking about Frank. Frank is a passionate open-source contributor and just got his bachelor’s degree three years ago. His journey in the open source world first started during his first year of university in 2015. One of the projects he created and open-sourced on GitHub had over 700 stars. “It has had hundreds of downloads per year, and many game developers use it to create demos”, Frank said.
After graduation, Frank joined the Flutter team at ByteDance and became one of the most active open-source contributors on the team, contributing a number of PRs to Dart and Flutter. Frank remembers that when he was working on the package size issues, he proactively followed up on a relevant issue on the Dart GitHub project and noticed that the Specializer component could use some further tuning. He created a patch with his improvements to the Dart compiler middleware and submitted it to the project. The patch wasn’t accepted initially because of the large number of code blocks affected and a few minor concerns. He modified the patch seven times before it was accepted, and it was merged into the code base a week later.
There are many other open-source enthusiasts like Frank in the Flutter team at ByteDance.
The ByteDance team summarized this passionate attitude toward innovation with the following words:
“There are indeed many people in the industry who prefer mature technology, but it takes time for every technology to mature, and there will always be people like us who love to stay on the cutting edge.”
This is especially true for something as novel as Flutter. There needs to be some daring people who take the first steps. At ByteDance, the Flutter engineering team, as well as the engineering teams that they support, actively try and embrace new technologies. Doing this benefited ByteDance tremendously and greatly increased our productivity.
ByteDance has always wanted to be part of things that could push the industry forward, and Flutter is likely to be one of those things.
我們還對兩個 Web 渲染器都進行了改進。對於 HTML,我們加入了對 字體功能 的支援,以啟用設定 FontFeature 以及使用畫布 API 渲染文字,以便在懸停時文字顯示在正確的位置。對於 HTML 和 CanvasKit,我們加入了對著色器遮罩和 computeLineMetrics 的支援,解決了 Flutter Web 和行動應用程式之間的奇偶性差距。例如,開發人員現在可以使用 不透明遮罩 透過著色器遮罩執行淡出轉場,並像在行動應用程式中一樣使用 computeLineMetrics。
對於 Flutter Web 以及 Flutter 本身,無障碍功能是我們的首要任務之一。按照設計,Flutter 透過建立 SemanticsNode 樹來實作無障碍功能。當 Flutter Web 應用程式使用者啟用無障碍功能時,框架會產生一個與 RenderObject DOM 樹平行的 DOM 樹,並將語義屬性轉換為 Aira。在此版本中,我們改進了語義節點位置,以在使用轉換時縮短行動和桌面 Web 應用程式之間的差距,這意味著當 Widget 透過轉換進行樣式設定時,焦點框應正確顯示在元素之上。若要實際查看此功能,請查看 Victor Tsaran 的這個影片,他在影片中使用 VoiceOver 和 Flutter Gallery App:
我們還透過命令列標誌在配置文件和發行模式下公開了語義節點除錯樹,以幫助開發人員透過視覺化為其 Web 應用程式建立的語義節點來除錯無障碍功能。
若要為您自己的 Flutter Web 應用程式啟用此功能,請執行以下操作:
1 2
$ flutter run -d chrome --profile --dart-define=FLUTTER_WEB_DEBUG_SHOW_SEMANTICS=true
拍攝照片: 我們使用了內建的 HtmlElementView,它使用平台視圖將原生 Web 元素渲染為 Flutter Widget。在此專案中,我們將 VideoElement 渲染為一個原生 HTML 元素,這就是您在拍攝照片之前在螢幕上看到的內容。我們使用 CanvasElement,它被渲染為另一個 HTML 元素。這使我們可以在您點擊拍攝照片按鈕時從媒體流中捕獲圖片。
這個專案很好地體現了以 Web 為中心的應用程式建立方法。我們驚訝地發現,與我們使用 Flutter 建立行動應用程式的體驗相比,建立此 Web 應用程式的流程是如此相似。我們必須考慮視窗大小、響應式、觸控與滑鼠輸入、圖片加載時間、瀏覽器相容性以及建立 Web 應用程式時需要考慮的所有其他事项。但是,我們仍然使用相同的模式、架構和編碼標準編寫 Flutter 程式碼。在為 Web 建立應用程式時,我們感到賓至如歸。Flutter 套件的工具和不斷發展的生態系統,包括 Firebase 工具套件,讓 I/O 照片亭成為可能。