In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-15 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >
Share
Shulou(Shulou.com)05/31 Report--
This article shows you how to deeply understand the compilation principle and optimization of flutter. The content is concise and easy to understand, which will definitely brighten your eyes. I hope you can get something through the detailed introduction of this article.
Problem background
What is Flutter for developers? What language is it written in, which parts are included, and how is it compiled and run on the device? How does Flutter make the Debug schema Hot Reload change quickly, the native experience of the Release schema? What is the difference between the Flutter project and our Android/iOS project, what is the relationship, and how is the Android/iOS embedded? How does the rendering and event passing mechanism of Flutter work? Does Flutter support hot updates? Flutter officially does not provide armv7 support under iOS, is it true? When using Flutter, if you find the bug of engine, how to modify and take effect? How do you locate, modify, and take effect if you build slowly or go wrong?
All of these require a global view of Flutter from design, development and build to final operation.
The editor will take a simple hello_flutter as an example to introduce the related principles of Flutter and its customization and optimization.
Introduction to Flutter
The architecture of Flutter is mainly divided into three layers: Framework,Engine and Embedder.
Framework is implemented in dart, including Material Design style Widget,Cupertino (for iOS) style Widgets, text / picture / button and other basic Widgets, rendering, animation, gestures, etc. The core code of this part is: flutter package under flutter warehouse and io,async,ui under sky_engine warehouse (dart:ui library provides the interface between Flutter framework and engine) and other package.
Engine is implemented using C++, which mainly includes Skia,Dart and Text. Skia is an open source two-dimensional graphics library, which provides a general API for a variety of software and hardware platforms. It has been used as the graphics engine of Google Chrome,Chrome OS,Android, Mozilla Firefox, Firefox OS and many other products, and the supporting platform also includes Windows7+,macOS 10.10.5, Android 4.1, Android, Firefox OS and so on. The Dart section mainly includes: Dart Runtime,Garbage Collection (GC) and, if it is Debug mode, JIT (Just In Time) support. In Release and Profile mode, it is AOT (Ahead Of Time) that compiles into native arm code, and there is no JIT part. Text is text rendering, and its rendering hierarchy is as follows: derived from minikin's libtxt library (for font selection, delimiting lines). HartBuzz is used for glyph selection and shaping. Skia, as the render / GPU backend, renders using FreeType on Android and Fuchsia, and CoreGraphics on iOS to render fonts.
Embedder is an embedded layer, that is, embed Flutter into various platforms. The main work done here includes rendering Surface settings, thread settings, and plug-ins. From this, we can see that the platform-related layer of Flutter is very low, the platform (such as iOS) only provides a canvas, and all the remaining rendering-related logic is within Flutter, which makes it cross-end consistency.
Flutter engineering structure
This article uses the development environment flutter beta v0.3.1, the corresponding engine commit:09d05a389.
Take the hello_flutter project as an example, the structure of the Flutter project is as follows:
Ios is part of iOS code, CocoaPods is used to manage dependencies, android is part of Android code, Gradle is used to manage dependencies, lib is dart code, and pub is used to manage dependencies. Similar to the Podfile and Podfile.lock,pub corresponding to Cocoapods in iOS, there are pubspec.yaml and pubspec.lock.
Flutter mode
For Flutter, it supports common patterns such as debug,release,profile, but it is different.
Debug mode: corresponds to Dart's JIT mode, also known as check mode or slow mode. Support for devices, simulators (iOS/Android), which opens assertions in this mode, including all debugging information, service extensions and Observatory debugging aids. This pattern is optimized for rapid development and operation, but not for execution speed, package size, and deployment. In Debug mode, compilation uses JIT technology to support the popular subsecond stateful hot reload.
Release mode: corresponds to Dart's AOT mode, which is aimed at deployment to end users. Only real machines are supported, not simulators. All assertions are closed, debugging information is removed as much as possible, and all debugging tools are closed. The package size is optimized for fast startup and fast execution. All debugging aids and service extensions are prohibited.
Profile mode: similar to Release mode, but with additional support for service extensions for Profile mode, support for tracing, and minimizing dependencies required to use trace information, for example, observatory can connect to processes. The reason Profile does not support simulators is that diagnostics on simulators do not represent real performance.
In view of the fact that there is no difference in compilation principles between Profile and Release, this article only discusses Debug and Release modes.
In fact, the iOS/Android project under flutter is still a standard iOS/Android project in nature. Flutter only generates and embeds App.framework and Flutter.framework (iOS) by adding shell to BuildPhase, and compiles and embeds Flutter-related code into native App by adding flutter.jar and vm/isolate_snapshot_data/instr (Android) through gradle. Therefore, this paper mainly discusses the construction, operation and other principles introduced by flutter. Compiling target includes arm,x64,x86,arm64, but because the principle is similar, this article only discusses arm (unless otherwise noted, Android defaults to armv7).
Compilation and Operation of Flutter Code (iOS) compilation in Release mode
In release mode, the link constructed by the dart code in the iOS project under flutter is as follows:
Gen_snapshot is a dart compiler, which uses tree shaking (similar to dependency tree logic, which can generate the minimum package, and thus forbids the reflection feature supported by dart in Flutter). It is used to generate machine code in assembly form, and then generate the final App.framework through xcrun and other compilation tool chains. In other words, all dart code, including business code, three-party package code, the flutter framework code they rely on, will eventually become App.framework.
Tree shaking function is located in gen_snapshot, corresponding logic, see: engine/src/third_party/dart/runtime/vm/compiler/aot/precompiler.cc
The symbols in the dart code that eventually correspond to App.framework are as follows:
In fact, similar to the product under Android Release (see below), App.framework also contains four parts of kDartVmSnapshotData,kDartVmSnapshotInstructions,kDartIsolateSnapshotData,kDartIsolateSnapshotInstructions. Why does iOS use App.framework instead of Android's four files? The reason is that under iOS, due to system limitations, the Flutter engine cannot mark a memory page as executable at run time, while Android can.
Flutter.framework corresponds to the engine part of the Flutter architecture, as well as Embedder. In practice, Flutter.framework is located under / bin/cache/artifacts/engine/ios* in flutter warehouse, which is pulled from google warehouse by default. When custom modification is needed, it can be generated by downloading the engine source code and using the Ninja construction system.
The end products of Flutter-related code are App.framework (dart code generation) and Flutter.framework (engine). From the perspective of Xcode project, Generated.xcconfig describes the configuration information of Flutter-related environment, and then the new xcode_backend.sh of Build Phases in Runner project settings implements the copy of Flutter.framework (from the engine of Flutter warehouse to the Flutter directory under the root directory of Runner project) and embedding and App.framework compilation and embedding. The content related to Flutter in the resulting Runner.app is as follows:
Where flutter_assets is the related resource, and the code is App.framework and Flutter.framework under Frameworks.
Operation in Release mode
The rendering, event, and communication processing logic related to Flutter is as follows:
Compilation in Debug mode
The structure of flutter compilation in Debug mode is similar to that of Release mode, and the differences are mainly shown in two points:
1.Flutter.framework
Because it is Debug, there is JIT support in Framework in this mode, while there is no JIT part in Release mode.
2.App.framework
Unlike App.framework in AOT mode, which is the local machine code corresponding to Dart code, in JIT mode, App.framework has only a few simple API, and its Dart code exists in the snapshot_blob.bin file. This part of the snapshot is a snapshot of the script, which contains the simple tagged source code. All comments, white space characters are removed, constants are normalized, and there is no machine code, tree shaking or confusion.
The symbol table in App.framework is as follows:
The call stack of the main entry in Debug mode is as follows:
The vm/isolate_snapshot_data/instr contents are all arm instructions, which will be loaded by engine at run time and marked as executable by vm/isolate_snapshot_instr. Services such as runtime (such as gc) are involved in vm_, which are used to initialize DartVM. For more information on the call entry, please see Dart_Initialize (dart_api.h). Isolate__ corresponds to our App code, which is used to create a new isolate. For the call entry, see Dart_CreateIsolate (dart_api.h). Flutter.jar is similar to iOS's Flutter.framework, including the code for the engine section (libflutter.so in Flutter.jar), and a set of classes and interfaces that embed Flutter into Android (FlutterMain,FlutterView,FlutterNativeView, etc.). In practice, flutter.jar is located under / bin/cache/artifacts/engine/android* in flutter warehouse, which is pulled from google warehouse by default. When custom modifications are needed, you can download the engine source code and use Ninja to build the system to generate flutter.jar.
Taking isolate_snapshot_data/instr as an example, the result of executing the disarm command is as follows:
)
Its Apk structure is as follows:
After a new installation of APK, it is decided whether to copy the assets in APK based on the judgment of a ts (versionCode in packageinfo combined with lastUpdateTime), as shown below:
Compilation in Debug mode
Similar to the difference of Debug/Release in iOS, the difference between Debug of Android and Release mainly includes the following two parts:
1.flutter.jar
Different from iOS
2.App code section
The snapshot_blob.bin under flutter_assets is the same as iOS.
After introducing the principle of Flutter compilation under iOS/Android, the following focuses on how to customize flutter/engine to complete customization and optimization. In view of the fact that Flutter is in an agile iteration, the current problem is not necessarily a problem, so this part is not to solve how many problems, but to select different types of problems to illustrate the solution.
Customization and optimization related to Flutter construction
Flutter is a very complex system, in addition to the above mentioned three-tier architecture, but also includes Flutter Android Studio (Intellij) plug-ins, pub warehouse management and so on. But our customization and optimization are often related to the tool chain of flutter, and the specific code is located in the flutter_tools package of the flutter repository. Next, give an example of how to customize this section.
Android part
Related contents include flutter.jar,libflutter.so (under flutter.jar) and gen_snapshot,flutter.gradle,flutter (flutter_tools).
1. Limit target to armeabi in Android
This section is related to the build, and the logic is under flutter.gradle. When App supports armv7/arm64 through armeabi, you need to modify the default logic of flutter. As follows:
Because of the characteristics of gradle itself, this part can be built directly after modification.
two。 Set the first launchable-activity to be used by default when Android starts
This part is related to flutter_tools and is modified as follows:
The focus here is not on how to modify it, but on how to make it effective. In principle, commands such as flutter run/build/analyze/test/upgrade actually execute the script flutter (flutter_repo_dir/bin/flutter), and then execute flutter_tools.snapshot through dart (generated through packages/flutter_tools) through the script. The logic is as follows:
If [[!-f "SNAPSHOT_PATH"] | | [!-s "STAMP_PATH"] | | [["(cat" STAMP_PATH ")"! = "revision"]] | | [["FLUTTER_TOOLS_DIR/pubspec.yaml"-nt "$FLUTTER_TOOLS_DIR/pubspec.lock"]] Then rm-f "$FLUTTER_ROOT/version" touch "$FLUTTER_ROOT/bin/cache/.dartignore"$FLUTTER_ROOT/bin/internal/update_dart_sdk.sh" echo Building flutter tool... If [["$TRAVIS" = = "true"]] | | ["$BOT" = = "true"]] | | [["$CONTINUOUS_INTEGRATION" = = "true"]] | | [["$CHROME_HEADLESS" = = "1"]] | [["$APPVEYOR" = = "true"]] | | ["$CI" = "true"]] Then PUB_ENVIRONMENT= "$PUB_ENVIRONMENT:flutter_bot" fi export PUB_ENVIRONMENT= "$PUB_ENVIRONMENT:flutter_install" if [[- d "$FLUTTER_ROOT/.pub-cache"]]; then export PUB_CACHE= "${PUB_CACHE:-" $FLUTTER_ROOT/.pub-cache "}" fi while: Do cd "$FLUTTER_TOOLS_DIR"$PUB" upgrade-- verbosity=error-- no-packages-dir & & break echo Error: Unable to 'pub upgrade' flutter tool. Retrying in five seconds... Sleep 5 done "$DART"-- snapshot= "$SNAPSHOT_PATH"-- packages= "$FLUTTER_TOOLS_DIR/.packages"$SCRIPT_PATH" echo "$revision" > "$STAMP_PATH" fi
It is not hard to see that to rebuild flutter_tools, you can delete the flutter_repo_dir/bin/cache/flutter_tools.stamp (so regenerate again) or block the if/fi judgment (regenerated each time).
3. How to use flutter of release mode in Android project Debug mode
When developers find that there are some jams in flutter during research and development, they guess that it may be the reason of logic, or it may be because it is flutter under Debug. At this point, you can build the apk under release, or you can force the flutter to be modified to release mode as follows:
IOS part
Related contents include: Flutter.framework,gen_snapshot,xcode_backend.sh,flutter (flutter_tools).
1. Recompilation caused by repeated replacement of Flutter.framework during optimized build
This part of the logic is related to construction, and is located in xcode_backend.sh. In order to ensure that Flutter gets the correct Flutter.framework each time, it will find and replace Flutter.framework based on configuration (see Generated.xcconfig configuration). However, this also results in the recompilation of some code that depends on this Framework in the project, as modified as follows:
two。 How to use flutter of release mode in iOS project Debug mode
You only need to change the FLUTTER_BUILD_MODE in Generated.xcconfig to release,FLUTTER_FRAMEWORK_DIR and the path corresponding to release.
Support for 3.armv7
For the original article, please see: https://github.com/flutter/engine/wiki/iOS-Builds-Supporting-ARMv7
As a matter of fact, flutter itself supports armv7 under iOS, but currently there is no official support. You need to modify the relevant logic on your own, as follows:
a. The default logic can generate Flutter.framework (arm64)
b. Modify flutter so that flutter_tools can be rebuilt each time, modify build_aot.dart and mac.dart, change the related arm64 for iOS to armv7, and change gen_snapshot to i386 architecture.
The gen_snapshot under the i386 architecture can be generated by the following command:
. / flutter/tools/gn-- runtime-mode=debug-- ios--ios-cpu=armninja-C out/ios_debug_arm
Here is an implicit logic:
Build gen_snapshot 's CPU-related predefined macros (_ _ x86_64__/__i386, etc.), target gen_snapshot 's arch, and the final App.framework 's architecture should be consistent as a whole. That is, x8634-> x86x64-> arm64 or i386-> i386-> armv7.
c. On iPhone4S, an EXC_BAD_INSTRUCTION (EXC_ARM_UNDEFINED) error occurs when gen_snapshot generates unsupported SDIV instructions, which can be achieved by adding a parameter to gen_snapshot-- no-use-integer-division (located in build_aot.dart). The logic behind it is shown in the following figure:
d. Based on the Flutter.framework generated by an and b, the lipo create is generated into Flutter.framework that supports both armv7 and arm64.
e. Modify the Info.plist under Flutter.framework and remove it
UIRequiredDeviceCapabilities arm64
By the same token, the same should be done for App.framework to avoid being affected by App Thining after it is on the shelf.
Debugging of flutter_tools
For example, if you want to know how flutter implements the logic when building apk in debug mode, you can follow the following ideas:
a. Understand the command line parameters of flutter_tools
b. Open packages/flutter_tools as a dart project, modify the flutter_tools.dart based on the obtained parameters, and set the command line dart app to start debugging.
Customized engine and debugging
Suppose we carry out customization and business development on the basis of flutter beta v0.3.1, and in order to ensure stability, we do not upgrade SDK within a certain period, and at this time, flutter modifies the bug that exists on v0.3.1 on master and marks it as fix_bug_commit. How can this situation be tracked and managed?
1.flutter beta v0.3.1 specifies that its corresponding engine commit is: 09d05a389, see flutter/bin/internal/engine.version.
two。 Get the engine code
3. Because what we get in 2 is the master code, and what we need is the code base corresponding to a specific commit (09d05a389), we pull out a new branch from this commit: custom_beta_v0.3.1.
4. Based on custom_beta_v0.3.1 (commit:09d05a389), execute gclient sync to get all the engine code corresponding to flutter beta v0.3.1.
5. Use git cherry-pick fix_bug_commit to synchronize master changes to custom_beta_v0.3.1, and compilation may fail if the changes have many dependencies on the latest changes.
6. Execute the following code for iOS-related modifications:
/ flutter/tools/gn-- runtime-mode=debug-- ios--ios-cpu=armninja-C out/ios_debug_arm./flutter/tools/gn-- runtime-mode=release-- ios--ios-cpu=armninja-C out/ios_release_arm./flutter/tools/gn-- runtime-mode=profile-- ios--ios-cpu=armninja-C out/ios_profile_arm./flutter/tools/gn-- runtime-mode=debug-- ios--ios-cpu=arm64ninja-C out/ios_debug./flutter/tools/ Gn-- runtime-mode=release-- ios--ios-cpu=arm64ninja-C out/ios_release./flutter/tools/gn-- runtime-mode=profile-- ios--ios-cpu=arm64ninja-C out/ios_profile
The product of arm/arm64&debug/release/profile for iOS can be generated. Flutter.framework and gen_snapshot under flutter/bin/cache/artifacts/engine/ios* can be replaced with build products.
If you need to debug the Flutter.framework source code, the build command is as follows:
. / flutter/tools/gn-- runtime-mode=debug-- unoptimized-- ios--ios-cpu=arm64ninja-C out/ios_debug_unopt
You can debug the engine source code by replacing Flutter.framework and gen_snapshot in flutter with the generated product.
7. Execute the following code for Android-related modifications:
/ flutter/tools/gn-- runtime-mode=debug-- android--android-cpu=armninja-C out/android_debug./flutter/tools/gn-- runtime-mode=release-- android--android-cpu=armninja-C out/android_release./flutter/tools/gn-- runtime-mode=profile-- android--android-cpu=armninja-C out/android_profile
The product of arm&debug/release/profile for Android can be generated. Gen_snapshot and flutter.jar under flutter/bin/cache/artifacts/engine/android* can be replaced with build products.
The above content is how to deeply understand the compilation principle and optimization of flutter. Have you learned the knowledge or skills? If you want to learn more skills or enrich your knowledge reserve, you are welcome to follow the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.