Exploration of Flutter Hot Update Technology

1. Preface

Flutter hot update technology refers to the ability to update application code and resources without recompiling and redeploying the application. This allows developers to quickly fix bugs, add new features, and improve application performance without requiring users to redownload and reinstall the application.In the era of AI, using AI to generate high-quality code is a standout feature. Take AI Design for example, a tool that effortlessly converts screenshots into editable Figma UI designs. Simply upload an image of any app or website, and it smoothly handles the conversion process. Additionally, AI Code enhances this functionality by facilitating Figma-to-Code translations, supporting a wide range of platforms including Flutter for Unified Cross-Platform Development on Android, iOS, and macOS, and ensures the creation of highly accurate code.

2. Analysis of Flutter Hot Update Technology Directions

After analysis, there are currently three feasible hot update solutions:

  1. A solution similar to the React Native (RN) framework
  2. Dynamic component framework for pages
  3. Custom Dart Virtual Machine solution

The following table compares these three solutions:

Solution NamePrincipleAdvantagesDisadvantages
RN-like solutionWritten in JavaScript, converted to Flutter atomic widgetsiOS natively supports JS, enabling updatesPerformance impact, high learning cost, Android requires JS library
Dynamic component framework for pagesCompile-time instrumentation, dynamic JSON delivery, data matching to replace WidgetSupports Android/iOS updatesUI updates easy, business logic difficult, high maintenance cost
Custom Dart VM solutionModify Flutter Engine to implement hot updatesSmall performance impact, high dynamismNeed to maintain different versions of the Flutter engine

This article will focus on the third solution - the Custom Dart Virtual Machine solution.

3. Preliminary Knowledge

Before delving into hot update technology, we need to understand some basic concepts:

3.1. Flutter Compilation Modes

Flutter's compilation modes come from Dart's compilation modes, mainly divided into JIT (Just In Time) and AOT (Ahead Of Time).

Compilation Mode NameFeaturesAdvantagesDisadvantages
JITJust-in-time compilation, like the V8 engineDynamic code execution, provides dynamic contentLarge compiler overhead, poorer performance
AOTPre-compilation, like GCCFast load and execution speedDifferent CPU architectures, larger binary packages, iOS cannot dynamically update

Flutter has three compilation modes: Debug, Release, and Profile:

  • Debug mode corresponds to JIT, supports devices and simulators, and supports rapid development and HotReload.
  • Release mode corresponds to AOT, supports real devices, and optimizes package size and execution speed.
  • Profile mode is similar to Release but retains debugging features to aid in performance analysis.

3.2. Analysis of Flutter Compilation Artifacts

On iOS and Android platforms, a Flutter project is essentially a standard native project. On the iOS platform, App.framework and Flutter.framework are generated through shell scripts in BuildPhase; on the Android platform, flutter.jar and compiled binary files are added through Gradle.

Analysis of the engine layer structure and compilation artifacts shows that, whether on iOS or Android, the APP business code consists of four segments: kDartVmSnapshotData, kDartVmSnapshotInstructions, kDartIsolateSnapshotData, and kDartIsolateSnapshotInstructions. In theory, as long as the loaded code and data segments can be dynamically replaced, hot updates can be achieved.

Currently, there are mainly two Flutter hot update solutions:

  • Hot Restart: Hot Restart refers to reloading application code and resources without recompiling the application. This is a quick and simple hot update method, but it can only update the application's code and resources, not the binary files.
  • Hot Reload: Hot Reload refers to reloading the application's binary files without recompiling the application. This is a more complex but more powerful hot update method that can update the application's code, resources, and binary files.

Implementation Principle:

The implementation principle of Flutter hot update technology is based on the incremental compilation feature of the Dart Virtual Machine (DVM). The DVM can compile Dart code into bytecode and store it in the application's binary files. When the application needs to be updated, the DVM can incrementally compile new Dart code and merge it with the application's binary files. Thus, the application can update its code and resources without recompilation.

4. Analysis of Hot Update Technology Solutions

4.1. Flutter Code Analysis

Based on the above analysis, we know that the Flutter code consists of four segments, among which kDartIsolateSnapshotData and kDartVmSnapshotData are allowed to be dynamically delivered, while kDartIsolateSnapshotInstructions and kDartVmSnapshotInstructions are not allowed to be dynamically delivered on iOS.

NameCommentFunctionNote
kDartIsolateSnapshotDataDart isolate data segmentClass information, global variables, function pointers, etc.Allowed to be dynamically delivered
kDartIsolateSnapshotInstructionsDart isolate instruction segmentContains AOT code executed by Dart isolateNot allowed to be dynamically delivered on iOS
kDartVmSnapshotDataVM isolate data segmentInitial state of the Dart heap shared between isolatesAllowed to be dynamically delivered
kDartVmSnapshotInstructionsVM isolate instruction segmentContains AOT instructions for common programs shared among all Dart isolates in the VMNot allowed to be dynamically delivered on iOS

Note: The meanings of isolate, snapshot, and vm isolate are explained as follows:

NameMeaning
isolateDart is single-threaded, and an isolate is similar to a thread in Dart. The difference between isolates and threads is that threads share memory, while isolates do not share memory. There are no lock contention issues, each Isolate executes independently, has its own event loop, and can only communicate through messages, with lower resource overhead than threads.
snapshotThe serialization of class information, global variables, and function instructions directly onto the disk is called a Snapshot.
vm isolateA process can have many isolates, but the heap areas of two isolates cannot be shared. The official design of the VM isolate (kDartVmSnapshot) is used to implement interactions between multiple isolates.

Flutter VM isolate

4.2. Runtime Analysis of Business Code Loading

The process of loading runtime code in Flutter is as follows:

  • On the Android side: The Flutter Engine loads libapp.so and flutter_assets.
  • On the iOS side: The Flutter Engine loads App.framework and Flutter.framework.

4.3. Compilation Generation of Business Code (Compile Time)

At compile time, we need to generate our own code segment and data segment files. The iOS build process compiles Dart code into assembly files through the compiler, and then generates App.Framework through the toolchain. The basic process is "Dart Code (business code)" -> (through Dart compiler gen_snapshot.cc) generates the assembly file snapshot_assemble.S -> (through xcrun tool) generates the obj file snapshot_assemble.o -> (through xcrun clang toolchain) generates App.Framework. The Android build process is similar, but there are simpler solutions, as shown in the following diagram:

Flutter Compile Code

4.4. Exploration of Hot Update Implementation Solutions

The core steps of hot update on the Android side are as follows:

  1. Modify Flutter Engine code: Customize the Flutter Engine to load libapp.so and flutter_assets from a specified path.

    // Example: Modify the Flutter Engine's loading path
    std::unique_ptr<flutter::AssetManager> asset_manager = std::make_unique<flutter::AssetManager>();
    asset_manager->PushBack(std::make_unique<flutter::APKAssetProvider>(...));
    
  2. Replace Flutter engine during APK compilation: Use the Gradle Transform plugin to dynamically replace the official Flutter engine during APK compilation.

    // Example: Use Transform plugin in Gradle script to replace Flutter engine
    android {
        ...
        transformations {
            replaceFlutterEngine {
                // Replacement logic
            }
        }
    }
    
  3. Generate patch package: Use the BSdiff algorithm to compare the old and new APK files and generate a patch package.

    # Example: Use bsdiff to generate a patch package
    bsdiff old.apk new.apk patch.bspatch
    
  4. Fetch patch package on APP startup: At application startup, access the backend interface and fetch the patch package based on parameters.

    // Example: Fetch patch package in Dart code
    Future<void> fetchPatch() async {
        final patch = await http.get('https://example.com/patch');
        if (patch.statusCode == 200) {
            // Save the patch package
        }
    }
    
  5. Verify and synthesize the patch package: Verify the md5, version number, and other information of the patch package and synthesize a new APK.

    // Example: Synthesize the patch package in Android code
    boolean applyPatch(String oldApk, String patch, String newApk) {
        return BsPatch.applyPatch(oldApk, patch, newApk);
    }
    
  6. Custom Flutter Engine loads resource files: Customize the Flutter Engine to load resource files from a specified path.

    // Example: Customize Flutter Engine to load resource files
    flutter::Settings settings;
    settings.assets_path = my_custom_assets_path;
    settings.icu_data_path = my_custom_icu_data_path;
    auto engine = std::make_unique<flutter::Engine>(settings);
    

5. Conclusion

Flutter hot update technology is a powerful tool that can help developers quickly fix errors, add new features, and improve application performance without requiring users to redownload and reinstall the application. This makes Flutter a very suitable framework for rapid iteration and updates of applications.

j