1.3 混合工程与持续集成
本节重点介绍Flutter 混合工程中解除Native 工程对Flutter 的直接依赖的具体实现方法。
1.3.1 背景思考
因为闲鱼采用的是Flutter 和Native 混合开发的模式,所以存在一部分开发人员只做Native 开发,并不熟悉Flutter 技术。
(1)如果直接采用Flutter 工程结构作为日常开发,则Native 开发人员也需要配置Flutter 环境,了解Flutter 技术,成本比较高。
(2)目前阿里巴巴集团的构建系统并不支持直接构建Flutter 项目,这也要求闲鱼解除Native 工程对Flutter 的直接依赖。
基于这两点考虑,闲鱼希望设计一个Flutter 依赖抽取模块,可以将Flutter 的依赖抽取为一个Flutter 依赖库并发布到远程,供纯Native 工程引用,如图1-16 所示。
图1-16
1.3.2 实现方法
1.Native 工程依赖的Flutter 分析
分析Flutter 工程,会发现Native 工程对Flutter 工程的依赖主要有三部分:
- Flutter 库和引擎。Flutter 的Framework 库和引擎库。
- Flutter 工程。我们自己实现的Flutter 模块功能,主要为在Flutter 工程lib 目录下,由Dart 代码实现的这部分功能。
- 自己实现的Flutter Plugin。
解开Android 和iOS 的App 文件,可以发现Flutter 依赖的主要文件如图1-17 所示。
图1-17
(1)Android 的Flutter 依赖的文件
- Flutter 库和引擎。包括icudtl.dat、libflutter.so,以及一些class 文件。它们都被封装在flutter.jar 中,这个jar 文件位于Flutter 库目录下的[flutter/bin/cache/artifacts/engine]中。
- Flutter 工程产物。包括isolate_snapshot_data、isolate_snapshot_instr、vm_snapshot_data、vm_snapshot_instr 和flutter_assets。
- Flutter Plugin 。各个Plugin 编译出来的AAR 文件, 包括:isolate_snapshot_data(应用程序数据段)、isolate_snapshot_instr(应用程序指令段)、vm_snapshot_data (虚拟机数据段)、vm_snapshot_instr(虚拟机指令段)。
(2)iOS 的Flutter 依赖的文件
- Flutter 库和引擎。Flutter.framework。
- Flutter 工程的产物。App.framework。
- Flutter Plugin。编译出来的各种Plugin 的Framework,以及图1-17 中的其他Framework。
我们只需要将编译结果抽取出来,打包成一个SDK 依赖的形式提供给Native 工程,就可以解除Native 工程对Flutter 工程的直接依赖。
2.Android 依赖的Flutter 库抽取
(1)Android 中Flutter 编译任务分析
Flutter 工程的Android 打包,其实只是在Android 的Gradle 任务中插入了一个flutter.gradle 任务,而flutter.gradle 主要做了三件事(这个文件可以在Flutter 库中的[flutter/packages/flutter_tools/gradle]目录下能找到):
- 增加flutter.jar 的依赖。
- 插入Flutter Plugin 的编译依赖。
- 插入Flutter 工程的编译任务,得到的产物包括两个isolate_snapshot 文件、两个vm_snapshot 文件和flutter_assets 文件夹。然后将产物拷贝到mergeAssets.outputDir,最后合并到APK 的assets 目录下。
(2)Android 的Flutter 依赖抽取实现
对Android 的Flutter 依赖抽取步骤如下:
(a)编译Flutter 工程
这部分的主要工作是编译Flutter 的Dart 和资源部分,可以用AOT 和Bundle 命令编译。
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
(b)将flutter.jar 和Flutter 工程的产物打包成一个AAR
主要工作是将flutter.jar 和第1 步编译的产物封装成一个AAR 文件。
添加flutter.jar 依赖。
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("api")) {
configuration = "api"
} else {
configuration = "compile"
}
add(configuration, project.files {
releaseFlutterJar
})
}
}
将Flutter 的产物合并到assets。
// 合并 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/**"
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)
(c)同时将AAR 文件和Flutter Plugin 编译出来的AAR 文件一起发布到Maven 仓库
发布Flutter 工程产物打包的AAR 文件。
echo 'Clean packflutter input(flutter build)'
rm -f -r android/packflutter/flutter/
# 拷贝flutter.jar
echo 'Copy flutter jar'
mkdir -p android/packflutter/flutter/flutter/android-arm-release &&
cp
flutter/bin/cache/artifacts/engine/android-arm-release/flutter.ja
r "$_"
# 拷贝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/* "$_"
# 将Flutter 库和flutter_app 打成AAR 文件,同时发布到Ali-maven
echo '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 ../
发布Flutter Plugin 的AAR 文件。
# 将Plugin 发布到Ali-maven
echo "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 ../
done
(d)纯粹的Native 项目只需要依赖我们发布到Maven 的AAR 文件即可。
在平时开发阶段,需要实时地依赖最新的AAR 文件,所以采用snapshot 版本。
configurations.all {
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}
ext {
flutter_aar_version = '6.0.2-SNAPSHOT'
}
dependencies {
//Flutter 主工程依赖:包含基于Flutter 开发的功能、Flutter 引擎lib
compile("com.taobao.fleamarket:IdleFishFlutter:${getFlutterAarVer
sion(project)}") {
changing = true
}
//其他依赖
}
static def getFlutterAarVersion(project) {
def resultVersion = project.flutter_aar_version
if (project.hasProperty('FLUTTER_AAR_VERSION')) {
resultVersion = project.FLUTTER_AAR_VERSION
}
return resultVersion
}