Author: Idle fish technology – Ran Tao

1. The introduction

In the previous article, “The Practice of Hybrid Engineering”, some students left comments asking for some details of the implementation of the removal of Flutter dependencies from a remote location, so this article will focus on some specific implementations of the direct removal of Flutter dependencies from the hybrid engineering.

2. Think about

At present, we are a hybrid development mode of Flutter and Native, so there are some students who only do the Native development and are not familiar with the technology of Flutter. (1) If the engineering structure of Flutter is directly used for daily development, these Native developers also need to configure the Flutter environment and understand some Flutter technologies, which will cost a lot. (2) Currently, the construction system of Ali Group does not support the direct construction of the Flutter project, which requires us to remove the direct dependence of Native Project on the Flutter. For these two reasons, we hope to design a Flutter dependency extraction module that can extract the Flutter dependencies into a Flutter dependency library and publish it remotely for pure Native projects to reference. As shown in the figure below:

3. The implementation

3.1 Native engineering dependent Flutter analysis

Through the analysis of the Project, we can find that the Native project relies on the Project of Flutter mainly in three parts: 1. Flutter library and engine: The Framework library and engine library of Flutter. 2. Project Flutter: We implemented the function of the Flutter module, mainly for the DART code in the Lib directory of the Project Flutter. 3. Self-implemented Flutter Plugin: Our self-implemented Flutter Plugin.

We undid the Android and iOS APP files and found that the main files that Flutter depends on are as follows:

Documents that Flutter depends on (Flutter products)

The Android Flutter depends on the following files: 1. The Flutter library and engine: icudtl.dat, libflutter. These are encapsulated in the flutter in the jar, this jar file is located in the flutter repository directory [flutter/bin/cache/artifacts/engine]. 2. Project products: isolate_snapshot_data, isolate_snapshot_instr, VM_snapshot_data, VM_snapshot_instr, flutter_assets. 3. Flutter Plugin: aar file compiled by each Plugin. Among them:

  • Isolate_snapshot_data Application data segment
  • Isolate_snapshot_instr Application instruction segment
  • Vm_snapshot_data VM data segment
  • Vm_snapshot_instr VM command segment

**1. 英 文 原 文 **2. 英 文 原 文 **3. ** compilation of various plugin frameworks, the other framework in the diagram

In that case, we only need to extract the compilation results of the three parts, package them into a form of SDK dependency and provide them to Native project, so that Native project can remove its direct dependence on the Flutter project.

3.2 Android dependent Flutter library extraction

3.2.1 Analysis of Flutter compilation tasks on Android

The Android package of the Project Flutter is just a Flutter. Gradle task inserted into the Android Gradle task. This Flutter. Gradle does three things: (This file can be found in the [Flutter /packages/flutter_tools/gradle] directory of the Flutter library.) 1. Increased reliance on the Flutter. Jar. 2. Insert the compilation dependency of the Flutter Plugin. 3. Insert the compilation task of the Flutter project and finally copy the products (two isolaate_snapshot files, two vm_snapshot files and flutter_assets folder) to Mergeassets.outputDir. Eventually merge into APK’s assets directory.

3.2.2 Android Flutter depends on extraction implementation

After understanding the Android version of the Flutter project, we extracted the Android Flutter dependency as follows: 1. Project Flutter. The main work of this part is to compile the DART and resources sections of the Flutter, which can be compiled using the AOT and Bundle commands.

echo "Clean old build"
find . -d -name "build" | xargs rm -rf
./flutter/bin/flutter clean

echo "Get packages"
./flutter/bin/flutter packages get

echo "Build release AOT"
./flutter/bin/flutter build aot --release --preview-dart-2 --output-dir=build/flutteroutput/aot
echo "Build release Bundle"
./flutter/bin/flutter build bundle --precompiled --preview-dart-2 --asset-dir=build/flutteroutput/flutter_assets
Copy the code

2. Package the Flutter. Jar and the products of the Flutter project into an AAR. The main work of this section is to encapsulate the flutter. Jar and the artifacts from the step 1 compilation into an AAR. (1) Add the flutter. Jar dependency

project.android.buildTypes.each {
    addFlutterJarImplementationDependency(project, releaseFlutterJar)
}
project.android.buildTypes.whenObjectAdded {
    addFlutterJarImplementationDependency(project, releaseFlutterJar)
}

private static void addFlutterJarImplementationDependency(Project project, releaseFlutterJar) {
    project.dependencies {
        String configuration
        if (project.getConfigurations().findByName("implementation")) {
            configuration = "implementation"
        } else {
            configuration = "compile"
        }
        add(configuration, project.files {
            releaseFlutterJar
        })
    }
}
Copy the code

(2) Merge the products of Flutter into assets

// merge flutter assets
def allertAsset ="${project.projectDir.getAbsolutePath()}/flutter/assets/release"
Task mergeFlutterAssets = project.tasks.create(name: "mergeFlutterAssets${variant.name.capitalize()}".type: Copy) {
    dependsOn mergeFlutterMD5Assets
    from (allertAsset){
        include "flutter_assets/**" // the working dir and its files
        include "vm_snapshot_data"
        include "vm_snapshot_instr"
        include "isolate_snapshot_data"
        include "isolate_snapshot_instr"
    }
    into variant.mergeAssets.outputDir
}
variant.outputs[0].processResources.dependsOn(mergeFlutterAssets)
Copy the code

2. Publish the AAR together with the AAR compiled by the Flutter Plugin to the Maven repository. (1) The AAR that publishes the Flutter engineering product package

echo 'Clean packflutter input(flutter build)'
rm -f -r android/packflutter/flutter/

#Copy flutter. The jar
echo 'Copy flutter jar'
mkdir -p android/packflutter/flutter/flutter/android-arm-release && cp flutter/bin/cache/artifacts/engine/android-arm-release/flutter.jar "$_"


#Copy the asset
echo 'Copy flutter asset'
mkdir -p android/packflutter/flutter/assets/release && cp -r build/flutteroutput/aot/* "$_"
mkdir -p android/packflutter/flutter/assets/release/flutter_assets && cp -r build/flutteroutput/flutter_assets/* "$_"

#Publish the Flutter library and Flutter_app to Ali-Maven as aarecho 'Build and publish idlefish flutter to aar' cd android if [ -n "$1" ] then ./gradlew :packflutter:clean :packflutter:publish -PAAR_VERSION=$1 else ./gradlew :packflutter:clean :packflutter:publish fi cd .. /Copy the code

(2) Aar that publishes the Flutter Plugin

#Publish plugin to Ali-Mavenecho "Start publish flutter-plugins" for line in $(cat .flutter-plugins) do plugin_name=${line%%=*} echo 'Build and publish plugin:' ${plugin_name} cd android if [ -n "$1" ] then ./gradlew :${plugin_name}:clean :${plugin_name}:publish -PAAR_VERSION=$1 else ./gradlew :${plugin_name}:clean :${plugin_name}:publish fi cd .. / doneCopy the code

3. For pure Native projects, we only need to compile and publish them to Maven aar. During the development phase, we need to be able to rely on the latest AAR in real time, so we use the SNAPSHOT version.

configurations.all {
    resolutionStrategy.cacheChangingModulesFor 0.'seconds'
}

ext {
    flutter_aar_version = '6.0.2 - the SNAPSHOT'
}

dependencies {
    // Main project dependencies of the Flutter: includes features developed based on the Flutter, the Flutter engine Lib
    compile("com.taobao.fleamarket:IdleFishFlutter:${getFlutterAarVersion(project)}") {
        changing = true
    }
    / /... Rely on other
}

static def getFlutterAarVersion(project) {
    def resultVersion = project.flutter_aar_version
    if (project.hasProperty('FLUTTER_AAR_VERSION')) {
        resultVersion = project.FLUTTER_AAR_VERSION
    }
    return resultVersion
}
Copy the code

3.3 Extraction of ios-dependent Flutter library

3.3.1 How to generate Flutter dependent files in iOS

Executing the “flutter build ios” command will eventually execute the flutter compilation script [xcode_backend.sh] which does the following:

1.Gets various parameters (such as project_path, target_path, build_mode, etc.) from various Generated. Xcconfig definitions.

2.Delete app. framework and app.flx from the Flutter directory.

3.Compare the Flutter/Flutter. Framework and{artifact_variant} directory with Flutter. Framework, if not equal, overwrite the former with the latter.

4.Obtain parameters (build_dir, local_engine_flag, preview_dart_2_flag, AOt_flags) required by the app. framework command.

5.Generate app. framework and copy the generated App.framework and AppFrameworkInfo.plist to the XCode project’s Flutter directory.

3.3.2 iOS Flutter depends on extraction implementation

The steps to extract the iOS Flutter dependency are as follows: 1. Compile the Flutter project to generate app. framework.

Echo "=== "./flutter/bin/flutter clean echo "=== ="./flutter/bin/flutter packages get echo /flutter/bin/flutter build ios --releaseCopy the code

2. Package each plug-in as a static library. There are two main steps: first, print the plugin into a binary file, and second, print the plugin’s registration entry into a binary file.

Echo "=== "to generate binaries for each plugin ===" CD ios/Pods#/usr/bin/env xcrun xcodebuild clean
#/usr/bin/env xcrun xcodebuild build -configuration Release ARCHS='arm64 armv7'BUILD_AOT_ONLY=YES VERBOSE_SCRIPT_LOGGING=YES -workspace Runner.xcworkspace -scheme Runner BUILD_DIR=.. /build/ios -sdk iphoneosFor plugin_name in ${plugin_arr} do echo "generate ${plugin_name}.a..." /usr/bin/env xcrun xcodebuild build -configuration Release ARCHS='arm64 armv7' -target ${plugin_name} BUILD_DIR=.. /.. /build/ios -sdk iphoneos -quiet /usr/bin/env xcrun xcodebuild build -configuration Debug ARCHS='x86_64' -target ${plugin_name} BUILD_DIR=.. /.. /build/ios -sdk iphonesimulator -quiet echo "${plugin_name}.a..." lipo -create ".. /.. /build/ios/Debug-iphonesimulator/${plugin_name}/lib${plugin_name}.a" ".. /.. /build/ios/Release-iphoneos/${plugin_name}/lib${plugin_name}.a" -o ".. /.. /build/ios/Release- iphoneOS /${plugin_name}/lib${plugin_name}. A "done echo "===" for reg_enter_name in "Flutter_plugin_entrance" "flutter_service_register" do echo "generate lib${reg_enter_name}.a..." /usr/bin/env xcrun xcodebuild build -configuration Release ARCHS='arm64 armv7' -target ${reg_enter_name} BUILD_DIR=.. /.. /build/ios -sdk iphoneos /usr/bin/env xcrun xcodebuild build -configuration Debug ARCHS='x86_64' -target ${reg_enter_name} BUILD_DIR=.. /.. /build/ iphonesimulator echo "merge lib${reg_enter_name}.a..." lipo -create ".. /.. /build/ios/Debug-iphonesimulator/${reg_enter_name}/lib${reg_enter_name}.a" ".. /.. /build/ios/Release-iphoneos/${reg_enter_name}/lib${reg_enter_name}.a" -o ".. /.. /build/ios/Release-iphoneos/${reg_enter_name}/lib${reg_enter_name}.a" doneCopy the code

3. Upload the tags to the remote repository and generate new tags. 4. Pure Native projects only need to update their POD dependencies.

According to the above method, we can relieve Native project’s direct dependence on the Flutter project, but there are still some problems in daily development: 1. The update of the Flutter project, and the update of the remote dependency library is not timely. 2. During version integration, it is easy to forget to update remote dependent libraries, resulting in the version not integrating the latest Flutter function. 3. When the Flutter is developed on multiple lines at the same time, the version management is chaotic and the remote library is easy to be overwritten. 4. At least one student is required to continuously follow up the release, resulting in high labor costs. In view of these problems, we introduced our team’s CI automation framework to address them from two aspects: (We will share this in a later article) one is automation, which reduces labor costs and human error. On the other hand, do version control, do it in an automated way. ** First, ** automatically compile and publish the remote library corresponding to the Flutter project before building a pure Native project. The whole process does not require manual intervention. ** Secondly, during the development and test phase, a five-stage version number was adopted, and the last bit was automatically incrementing. This ensures that the version numbers of all parallel developed Flutter libraries during the test phase will not conflict. ** Finally, in the release stage, the version number of three or four sections is adopted, which can be consistent with the version number of the APP and facilitate the follow-up traceability of problems.

The whole process is shown in the figure below:

Build the process in Standalone mode

5. The last

Xianyu technical team is a short and concise engineering and technical team. We not only focus on the effective solution of business problems, but also push the cutting edge practice of breaking the constraints of the technology stack (the unification of android/iOS/Html5/Server programming model and language), computer vision technology on mobile terminals. As a software engineer in the Xianyu Technical team, you have the opportunity to demonstrate all of your talents and courage in the evolution of the entire product and user problem solving to prove that technology development is a power to change the way of life.

Resume delivery: [email protected]