Flutter add-to-app 实战:用纯 Android 宿主应用验证 Flutter Module 的 V2 Embedding 集成

发布时间:2026/9/7 3:33:16
Flutter add-to-app 实战:用纯 Android 宿主应用验证 Flutter Module 的 V2 Embedding 集成 Flutter add-to-app 实战用纯 Android 宿主应用验证 Flutter Module 的 V2 Embedding 集成【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter本篇技术指南围绕 Flutter 仓库中的纯 Android 宿主应用模板 android_host_app_v2_embedding 展开它是 devicelab 集成测试 用来验证「在已有 Android 应用中嵌入 Flutter Moduleadd-to-app」场景的宿主工程。读完后你将掌握如何用 V2 Embedding 编写一个只含原生代码的 Flutter 宿主 Activity、如何通过 Maven AAR 依赖把 Flutter Module 产物接入现有 Android 工程以及 Flutter CI 如何端到端地验证这条 add-to-app 构建链路。1. 模板的定位为flutter create -t module准备的宿主应用根据该目录的 README这个工程是一个「Android host app」它的用途明确$ flutter create -t module hello即先用上面的命令创建一个 Flutter Module 工程hello把它放在宿主应用的**同级目录sibling folder**中然后由宿主应用作为纯 Android 工程去依赖这个 Module 的构建产物。该模板被 devicelab 任务build_android_host_app_with_module_aar.dart直接引用用于回归验证「已有 Android App Flutter Module」这条 add-to-app 构建路径。所属目录 pure_android_host_apps 的整体定位也印证了这一点——它是「集成测试中用于测试 add-to-app 用例的最小 Android 应用集合」。所谓「纯pure」指宿主工程的源码里没有任何 Flutter 平台代码或 Flutter CLI 参与其构建Flutter 能力完全以依赖包形式注入这正是真实 add-to-app 场景的形态。2. V2 Embedding 的宿主 Activity整个工程最核心的两行代码宿主应用唯一的 Java 源文件是 MainActivity.javapackage io.flutter.add2app; import io.flutter.embedding.android.FlutterActivity; public class MainActivity extends FlutterActivity { }这里的关键在于导入的包名是io.flutter.embedding.android.FlutterActivity——这是 FlutterV2 Embedding的 Activity 基类V1 Embedding 位于io.flutter.app包下。继承它之后FlutterActivity会在运行时负责创建FlutterEngine、加载FlutterShellArgs并完成引擎与 Activity 的绑定所以业务侧不需要写任何引擎生命周期代码。对应的 AndroidManifest.xml 同样极简仅注册了这个 Activitymanifest xmlns:androidhttp://schemas.android.com/apk/res/android xmlns:toolshttp://schemas.android.com/tools application android:allowBackupfalse tools:ignoreGoogleAppIndexingWarning,MissingApplicationIcon activity android:name.MainActivity / /application /manifest注意这里没有io.flutter.embedding.android.FlutterEmbedding相关的 manifest 配置也无需声明插件权限——因为 Flutter 侧的配置引擎、插件、flutterProjectType元数据等随 Module 的 AAR 产物一起打进最终 APK由构建期合并。devicelab 测试后续会解包 APK 校验合并后的 manifest 中包含meta-data android:nameflutterProjectType android:valuemodule /这一元数据是 V2 Embedding 识别「当前宿主来自 Flutter Module」的关键标记见 build_android_host_app_with_module_aar.dart 第 300-308 行 的断言逻辑。3. 依赖注入方式用 AAR 坐标引入 Flutter Module 产物宿主工程与 Flutter Module 的衔接全部发生在 Gradle 依赖声明中。app/build.gradle 的内容如下apply plugin: com.android.application android { namespace io.flutter.add2app compileSdk 36 ndkVersion 28.2.13676358 // This version must exactly match the version of the NDK that the recipe pulls from CIPD. compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } defaultConfig { applicationId io.flutter.add2app minSdk 24 targetSdk 36 versionCode 1 versionName 1.0 } buildTypes { profile { initWith debug } } } dependencies { debugImplementation io.flutter.devicelab.hello:flutter_debug:1.0 profileImplementation io.flutter.devicelab.hello:flutter_profile:1.0 releaseImplementation io.flutter.devicelab.hello:flutter_release:1.0 }几个值得注意的实现细节分构建变体引用不同 AARdebug / profile / release 三个变体分别依赖flutter_debug、flutter_profile、flutter_release三个 Maven 坐标groupIdio.flutter.devicelab.hello。groupId 中的io.flutter.devicelab来自 CI 创建 Module 时传入的--org io.flutter.devicelab参数见 任务源码第 64-69 行即 AAR 的坐标由 Module 工程自己的--org和目录名决定宿主侧必须严格对齐。profile 变体buildTypes { profile { initWith debug } }显式声明了 profile 构建类型保证flutter_profileAAR 也有变体可挂载——这是 Module 模式支持 profile 构建性能分析的体现。NDK 版本强约束ndkVersion 28.2.13676358的注释明确要求它必须与 CI 配方recipe从 CIPD 拉取的 NDK 版本完全一致属于 CI 环境的硬约束本地复现该测试时同样需要满足。SDK 基线minSdk 24、targetSdk 36、compileSdk 36Java 17 编译选项代表当前 CI 所采用的 Android 构建基线。而 AAR 从哪里来根 settings.gradle 给出了仓库声明dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) repositories { google() mavenCentral() def flutterStorageUrl System.getenv(FLUTTER_STORAGE_BASE_URL) ?: https://storage.googleapis.com maven { url uri($flutterStorageUrl/download.flutter.io) } maven { url uri(../hello/build/host/outputs/repo) } // BEGIN ci only configuration for getting engine artifacts in pre-submit environments ... // END ci only configuration for getting engine artifacts in pre-submit environments } } include :app../hello/build/host/outputs/repo相对路径指向同级目录hello即 Flutter Module 工程的flutter build apk输出中的本地 Maven 仓库flutter_debug/profile/release三个 AAR 就是flutter build apk模块的 ephemeral host 构建发布在这里的。这解释了 README 中「Module 工程必须放在宿主同级目录」的原因。$flutterStorageUrl/download.flutter.ioFlutter 引擎构件如libflutter.so对应的引擎 AAR的标准下载仓库通过FLUTTER_STORAGE_BASE_URL环境变量可切换镜像。CI 专用段落从 Module 工程的.android/local.properties读取flutter.sdk路径再拼接bin/cache/engine.realm中的 realm 前缀追加一个 maven 仓库。从源码结构看这是 pre-submit 环境下引擎构件尚未发布到公共仓库、需要按 realm 从预发布地址取用时的兜底配置注释也明确标注为「ci only」。其余配置文件中gradle.properties 设置了-Xmx8G的 JVM 参数与android.useAndroidXtrue使用 AndroidX 的 V2 Embedding 工程必须开启。4.REPLACEME占位符模板为何故意不带具体版本号打开 gradle-wrapper.properties 和根 build.gradle会发现两处版本号被写成了占位符# gradle-wrapper.properties distributionUrlhttps\://services.gradle.org/distributions/gradle-REPLACEME-bin.zip// build.gradle根项目 buildscript { repositories { google() mavenCentral() } dependencies { classpath com.android.tools.build:gradle:REPLACEME } }这并不是漏写而是模板的刻意设计devicelab 任务在运行时用参数化版本原地替换REPLACEME见 任务源码第 239-253 行propertyContent.replaceFirst(REPLACEME, gradleVersion)与topBuildContent.replaceFirst(REPLACEME, agpVersion.toString())。这样可以用同一份宿主模板覆盖多组 Gradle / AGP 版本矩阵。当前 CI 实际运行两组组合见 任务入口 main()Gradle 版本AGP 版本覆盖场景8.48.2.1AGP 8.3 之前的旧链路8.13-rc-18.8.1AGP 8.3 及以后的新链路含 RC 版本兼容性任务中还针对agpVersion 8.3做了差异化断言AGP 8.3 起合并资源的中间产物目录多了一层mergeDebugAssets/mergeReleaseAssets路径第 310-328 行这是 AGP 版本升级对构建中间产物结构的直接影响。5. Devicelab 集成测试的完整验证链路模板本身只有几十个文件它的价值由 build_android_host_app_with_module_aar.dart 定义的端到端流程激活。该测试按以下顺序执行ModuleTest类buildTarget module-gradle查找 JavafindJavaHome()后续所有 Gradle 命令注入JAVA_HOME。创建 Flutter Moduleflutter create --org io.flutter.devicelab --templatemodule hello生成与 README 描述一致的 Module 工程。注入 FFI 包与只读资源创建一个名为ffi_package的本地 FFI 包加入 Module 依赖并在assets/下生成read-only.txt非 Windows 上chmod 444。这两个注入项用于后续验证原生.so与资源文件访问权限是否正确进入宿主 APK。构建 Flutter Module 库归档在 Module 的.android目录执行gradlew flutter:assembleDebug断言产出.android/Flutter/build/outputs/aar/flutter-debug.aar第 124-150 行。构建 ephemeral 宿主 APKflutter build apk检查build/host/outputs/apk/release/app-release.apk存在接着flutter clean后再构建一次验证 clean 后可重建editable 模式。验证flutter build aarflutter build aar --no-profile直接构建 AAR 成功。接入本文的宿主模板把dev/integration_tests/pure_android_host_apps/android_host_app_v2_embedding递归拷贝到临时目录的hello_host_app并从 Module 的.android目录拷入gradlew与gradle-wrapper.jar然后替换REPLACEME为指定 Gradle / AGP 版本第 217-253 行。构建宿主 debug APK 并校验内容gradlew app:assembleDebug后用 apk_utils 解包 APK 断言包含flutter_assets关键文件、lib/arm64-v8a/libflutter.so、libapp.so以及lib/arm64-v8a|armeabi-v7a/libffi_package.soFFI 包的两个 ABI 产物并校验 manifest 中的flutterProjectType module元数据。只读资源权限检查定位中间产物app/build/intermediates/assets/debug[/mergeDebugAssets]/flutter_assets/assets/read-only.txt要求文件权限以rw开头——即源文件是 444 只读进入 APK 后必须对用户可读否则运行时RootBundle加载会失败这是对 Gradle merge assets 行为的一次真实回归。构建宿主 release APK 并校验gradlew app:assembleRelease后检查app-release-unsigned.apk断言两个 ABI 的libflutter.so/libapp.so/libffi_package.so齐全并解包assets/flutter_assets/NOTICES.Zgzip utf8 解码后须包含skia与Flutter Authors字样验证开源许可证文件正确生成。构建过程日志断言检查 stderr 中不得出现You are applying Flutters main Gradle plugin imperatively——即要求宿主工程以声明式plugins {}/ 依赖方式而非命令式apply方式接入 Flutter Gradle 插件防止构建链路退化到旧写法。任一步失败任务即返回TaskResult.failurefinally块清理临时目录。测试由combine()将两组版本矩阵串行为一个TaskFunction顺序执行。6. 对实际 add-to-app 场景的可迁移结论从这个模板和测试中可以提炼出接入真实项目的几条经验宿主侧的最小实现就是一个继承io.flutter.embedding.android.FlutterActivity的 Activity 一条 Activity 声明其余 Flutter 配置随 AAR 合并这大幅降低了原生团队的理解成本。Module 与宿主的耦合点是 Maven 坐标与仓库路径宿主依赖org.module名:flutter_variant:1.0这样的坐标并需要能访问 Module 发布的本地仓库build/host/outputs/repo以及download.flutter.io引擎构件仓库两者缺一都会导致依赖解析失败。版本矩阵意识模板用REPLACEME占位、由 CI 注入 Gradle/AGP 版本的做法说明 add-to-app 构建链路必须同时对新旧 AGP尤其 8.3 前后的产物结构差异保持兼容自己维护宿主工程时升级 AGP/Gradle 后应回归「APK 内产物清单 manifest 元数据 资源权限」这三类检查。验证手段可复用测试中「解包 APK 检查 so 文件与资源权限、解包 NOTICES.Z 检查许可内容、校验flutterProjectType元数据」这些手法同样适用于日常排查「为什么我的宿主 APK 里缺 flutter 产物」这类问题。相关入口文件宿主模板 README、宿主 MainActivity、宿主依赖声明、宿主仓库配置、devicelab 测试任务。【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考