在 Flutter 混合工程体系 一文中,阐述了Flutter 三种开发模式,在实际业务中搭建持续集成时,我们更希望发本地开发使用混合模式,持续集成使用解耦模式, 主要是解决以下两个问题:
- 混合模式:开发调试方便,包括热更新, Native 与 Flutter 开发源码断点调试
- 解耦模式:不侵入 Android /iOS Native工程,不对 Flutter 环境产生依赖,可以单独独立构建打包
我们这里重点阐述 解耦模式,要对 Android 与 Flutter 进行解藕,就需要分析混合模式最终生成的 APK 内容长啥样和 Flutter 构建脚本。
Android Flutter APK 文件结构分析
我们通过 Android Studio 菜单 Build -> Analyze APK 选择构建好的 Release APK 包,可以看到如下内容:

通过上面 APK 结构,我们会发现Flutter 项目比普通的Android 项目多了如下几个文件:
以下文件说明参考 https://blog.csdn.net/weixin_34001430/article/details/87942062
- lib/armeabi-v7a/libflutter.so - Flutter 引擎
- isolate_snapshot_instr - 应用程序指令段
- isolate_snapshot_data - 应用程序数据段
- vm_snapshot_instr - DartVM 指令段
- vm_snapshot_data - DartVM 数据段
- flutter_assets - 应用程序静态资源文件与配置
Flutter 项目 Gradle 构建脚本分析
在 Flutter Android 工程里面,我们在 build.gradle 文件里面会看到 Flutter gradle 构建脚本:
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
进入 flutter.gradle 里面,我们会看到相关 flutter 的打包构建流程,主要包括三个部分:
- apply 方法:flutter 引擎平台架构(arm, x86) 库处理

- buildBundle 方法:flutter aot 和 bundle 编译处理,也就是上面所说的 snapshot 相关flutter 构建产物文件

运行以后会发现其实执行的如下两条命令:
// Debugflutter build bundle --debug// releaseflutter build aot --release
- getAssets: 构建产物处理, 把 flutter snapshot 构建产物复制到 assets 目录,刚好与上面 APK 文件结构分析对应起来了。

Android Flutter 解耦处理
通过上面的 APK 文件和 Gradle 构建脚本分析,我们很清楚的知道了整个 Flutter 构建打包以及与Native 合并的过程, 接下来我们来进行 Android Native 工程 和 Flutter Module 工程解耦处理。
Android Native 项目依赖和构建配置处理
- 移除 Android Native 项目 ${root}/setting.gradle 对 flutter 构建的脚本的依赖
// 需要移除evaluate(new File(settingsDir.parentFile,'happyflutter/.android/include_flutter.groovy'))
- 移除 Android Native 项目 ${root}/app/build.gradle 对 flutter 引擎库的依赖
dependencies {implementation project(':flutter') // 需要删除}
- Android Native 项目 ${root}/app/build.gradle 添加对 libs 文件夹的 .jar .aar 库
dependencies {// implementation project(':flutter')implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])}
Flutter Mobule 打包为独立的 .aar 文件 gradle 脚本编写
- 修改 Flutter Module 项目构建类型 com.android.application 为 com.android.library
- 简化 $flutterRoot/packages/flutter_tools/gradle/flutter.gradle 构建逻辑
- ${root}/android/app/build.gradle 完整内容如下 ```bash import java.nio.file.Path import java.nio.file.Paths
apply plugin: ‘com.android.library’
android { compileSdkVersion 28
compileOptions {sourceCompatibility 1.8targetCompatibility 1.8}defaultConfig {minSdkVersion 16targetSdkVersion 28versionCode 1versionName "1.0"testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// ndk { // abiFilters ‘x86’, ‘x86_64’, ‘armeabi-v7a’, ‘armeabi-v8a’, ‘armeabi’ // } }
buildTypes {profile {initWith debug}release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}sourceSets {main {jniLibs.srcDirs "jniLibs"}}
}
// ${project.projectDir.getAbsolutePath()} Properties properties = new Properties() properties.load(project.rootProject.file(‘local.properties’).newDataInputStream()) String targetArch = ‘arm’ if (project.hasProperty(‘target-platform’) && project.property(‘target-platform’) == ‘android-arm64’) { targetArch = ‘arm64’ } // def isDebug = gradle.startParameter.taskNames.contains(“assembleDebug”) // def platform = isDebug ? “android-x86” : “android-${targetArch}-release” def flutterRoot = properties.getProperty(‘flutter.sdk’) def flutterJarPath = “${flutterRoot}/bin/cache/artifacts/engine/android-${targetArch}-release/flutter.jar”
Path baseEnginePath = Paths.get(flutterRoot, “bin”, “cache”, “artifacts”, “engine”) File debugFlutterJar = baseEnginePath.resolve(“android-${targetArch}”).resolve(“flutter.jar”).toFile() File releaseFlutterJar = baseEnginePath.resolve(“android-${targetArch}-release”).resolve(“flutter.jar”).toFile() File flutterX86Jar = baseEnginePath.resolve(“android-x86”).resolve(“flutter.jar”).toFile() File flutterX64Jar = baseEnginePath.resolve(“android-x64”).resolve(“flutter.jar”).toFile()
//println flutterX86Jar.length() //println flutterX86Jar.parentFile
Task flutterX86JarTask = project.tasks.create(“FlutterX86Jar”, Jar) { from(“${flutterRoot}/bin/cache/artifacts/engine/android-x86/libflutter.so”) { into “lib/x86” } from(“${flutterRoot}/bin/cache/artifacts/engine/android-x64/libflutter.so”) { into “lib/x86_64” } }
project.android.buildTypes.each { addFlutterJarImplementationDependency(project, flutterX86JarTask, debugFlutterJar, releaseFlutterJar, flutterX86Jar, flutterX64Jar) } project.android.buildTypes.whenObjectAdded { addFlutterJarImplementationDependency(project, flutterX86JarTask, debugFlutterJar, releaseFlutterJar, flutterX86Jar, flutterX64Jar) }
private void addFlutterJarImplementationDependency(Project project, Task flutterX86JarTask, File debugFlutterJar, File releaseFlutterJar, File flutterX86Jar, File flutterX64Jar) { def isDebug = gradle.startParameter.taskNames.contains(“assembleDebug”) project.dependencies { String configuration if (project.getConfigurations().findByName(“implementation”)) { configuration = “implementation” } else { configuration = “compile” } add(configuration, project.files { if (isDebug) { [flutterX86JarTask, releaseFlutterJar] } else { releaseFlutterJar } }) } }
private Properties readPropertiesIfExist(File propertiesFile) { Properties result = new Properties() if (propertiesFile.exists()) { propertiesFile.withReader(‘UTF-8’) { reader -> result.load(reader) } } return result }
afterEvaluate { android.libraryVariants.all { def variant -> // println variant.mergeAssets.outputDir // println variant.buildType.debuggable
def flutterAsset = files("../../build")def flutterAOT = files("../../build/aot")def mergeFlutterAssets = project.tasks.create(name: "mergeFlutterAssets${variant.name.capitalize()}", type: Copy) {// dependsOn copySharedFlutterAssetsTaskdependsOn variant.mergeAssetsfrom (flutterAsset){include "flutter_assets/**"exclude{details ->details.file.name.contains('isolate_snapshot_data') ||details.file.name.contains('vm_snapshot_data') ||details.file.name.contains('kernel_blob.bin')}}from (flutterAOT){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)}
}
dependencies { implementation fileTree(dir: ‘libs’, include: [‘*.jar’]) implementation ‘com.android.support:appcompat-v7:28.0.0-alpha1’ testImplementation ‘junit:junit:4.12’ androidTestImplementation ‘com.android.support.test:runner:1.0.2’ androidTestImplementation ‘com.android.support.test.espresso:espresso-core:3.0.2’ }
<a name="wNs99"></a>### 编写 Flutter 插件和 Flutter Module 项目自动化 shell 脚本构建- 项目 AOT 和 Bundle 编译 ${root}/script/project.sh```bash#!/bin/bashroot=`pwd`. "${root}/script/util.sh"green "--root: ${root}, build mode: ${build}, run mode: ${run}"green "--start flutter aot and bundle build"flutter cleanif [[ $build = "debug" ]]then# green ">>flutter build aot --debug && flutter build bundle --debug"# android-arm does not support AOT compilation# flutter build aot --debug# flutter build bundle --debuggreen "--android-arm does not support AOT compilation, use release build aot"yellow ">>flutter build aot --release && flutter build bundle --debug"# android-arm does not support AOT compilationflutter build aot --releaseflutter build bundle --debugif [[ $build = 'debug' ]] && [[ $run = 'simulator' ]]; thencp -f "${root}/build/flutter_assets/vm_snapshot_data" "${root}/build/aot/vm_snapshot_data"cp -f "${root}/build/flutter_assets/isolate_snapshot_data" "${root}/build/aot/isolate_snapshot_data"green ">>when debug and simulator mode, copy snapshot successfully!"fiyellow ">>./gradlew assembleDebug"cd android && ./gradlew clean && ./gradlew assembleDebugcd ..elseyellow ">>flutter build aot --release && flutter build bundle --release"flutter build aot --releaseflutter build bundle --releaseyellow ">>./gradlew assembleRelease"cd android && ./gradlew clean && ./gradlew assembleReleasecd ..figreen "--flutter arr file[ ${root}/android/app/build/outputs/aar/app-${build}.aar ] build successfully!"cp -f "${root}/android/app/build/outputs/aar/app-${build}.aar" "${root}/publish/${build}"
- Flutter 插件 .aar 文件生成和处理 ${root}/script/plugin.sh
这里是直接从 Flutter 插件编译的缓存目录拷贝 .arr 文件。目前需要自己手动在 Flutter Module 项目目录下执行 flutter build apk —debug 和 flutter build apk —release 命令生成插件的 debug 和 release .arr 文件.
#!/bin/bashroot=`pwd`. "${root}/script/util.sh"echo "start flutter plugin aar copy......"for line in $(cat .flutter-plugins)doarr=(${line/=/ })name=${arr[0]}path=${arr[1]}plugin_name=`basename $path`plugin_dir=$(dirname $path)plugin=${path}androidaar="${path}android/build/outputs/aar/${name}-${build}.aar"if [ ! -f $arr ]thencp -f $aar "${root}/publish/${build}"elseecho "$aar is not exists"fidoneecho "flutter plugin copy successfully!"
- 拷贝 Flutter Module 生成的 .aar 文件给 Android Native 项目的 app/libs 目录 ${root}/script/copy.sh
HappyAndroid 为 Native 项目名称, Android Native 项目 和 Flutter Module 项目放到同一根目录下即可
#!/bin/bashroot=`pwd`. "${root}/script/util.sh"rm -rf "../HappyAndroid/app/libs/"cp -r "${root}/publish/${build}/" "../HappyAndroid/app/libs"
运行 shell 命令生成 .arr 文件 ${root}/script/build.sh
#!/bin/bashroot=`pwd`. "${root}/script/project.sh". "${root}/script/plugin.sh". "${root}/script/copy.sh"
- 生成 debug .aar 模拟器包: ./script/build.sh build=debug
- 生成 debug .aar 真机包: ./script/build.sh build=debug
- 生成 release .aar 真机包: ./script/build.sh build=release

最后,按照正常的 Android Native 工程运行方式运行项目,即可 Running!
常见问题
| 打包模式 | 运行模式 | 构建 | 依赖包 | 可运行 |
|---|---|---|---|---|
| Debug | 模拟器 | - flutter build aot —release - flutter build bundle —debug |
android-arm-release/flutter.jar 或 android-x64/flutter.jar |
✅ 条件:复制 flutter_assets 里面的isolate_snapshot_data 和 vm_snapshot_data 覆盖 aot 里面的 isolate_snapshot_data 和 vm_snapshot_data |
| 真机 | - flutter build aot —release - flutter build bundle —debug |
android-arm-release/flutter.jar | ✅ | |
| 真机 | - flutter build aot —release - flutter build bundle —release |
android-arm-release/flutter.jar | ✅ | |
| 真机 | - flutter build aot —release - flutter build bundle —debug |
android-arm/flutter.jar | ❌ Error:JIT runtime cannot run a precompiled snapshot |
|
| 真机 | - flutter build aot —debug - flutter build bundle —debug |
android-arm/flutter.jar 或 android-arm-release/flutter.jar |
❌ android-arm does not support AOT compilation |
|
| Release | 模拟器 | |||
| 真机 | - flutter build aot —release - flutter build bundle —release |
android-arm-release/flutter.jar | ✅ |
