flutter build aar 及一键打包aar并上传Maven私服(阿里云效)

引子

盼望着,盼望着,华为鸿蒙升级了。Harmoney OS Next 不再支持 Android 系统了,整个移动端瞬间炸了锅。

作为一个曾经手机市场王者,任何一家公司都无法忽视华为的市场,Android 阵营瞬间就出现了学习鸿蒙系统的激进力量。

同时,怕步子超大了,Harmoney OS Next 还是支持 flutter 跨端技术的。鉴于我一直时做跨端开发的,无论是 React Native、小程序,还是阿里系的 mist、weex 都多少有所涉猎,也就直接把三年前的 flutter 代码拉出来遛遛了:

github.com/hongyi0609/...

与此同时,也顺手建立另一个 my_fluter 库,把MeiTuanEdwin项目中 my_flutter 模块单独抽出来了:

github.com/hongyi0609/...

弄得有其他人合作开发似的~

背景

大家对郭霖大佬都不陌生吧?! 前一段我刷他的文章,发现他在研究 flutter,然后还分享了一篇读者投稿。以此得知,微软未来会有很多的 flutter 的业务,并且大部分处于起步阶段。所以郭老师就开始之前很少关注的 flutter 研究生涯。 这不是众里寻她千百度,蓦然回首,那人却在微信公众号吗? 我也就立马着手继续自己的 flutter 研究之路了。如果你对 flutter 一知半解,那我劝你先去读读这篇文章:juejin.cn/post/730657... 跟你想的基本一致,我就是读这篇文章的时候发现,搭建环境根本不是那么回事儿。

八卦:大厂是怎么玩儿的?

很多有电商味道的大厂,都是精细化操作,整个App 采用组件化开发,每个组件都是一个单独的小组 客户端+前端,其中客户端由 Android+iOS+跨端(外包) +小程序+h5组成,前端由小程序+h5+web 组成,这样整个大前端团队就形成了。 现在开始卷,客户端团队的架构组经过无数次的迭代重构,终于在 325 下来之前,憋出了 taro 、uniapp或者 morjs。由于实现了多端一体化,开发一次代码的成果可以在 Android、iOS、小程序、Web 端等所有平台运行,那么写**跨端(外包)**的同学就愉快的下岗了。同时,原来的小程序开发时微信、抖音、支付宝,每个平台一个主要负责同学,现在另外两个同学只要不去劳动局都好商量。 你可能会问,剩余的技术空缺谁来补上。听好啊: 下一年的 KPI主要是两个方向, 1. 客户端同学除了会用 Android、iOS 开发,也要具备跨端开发的能力,并且要互为 backup 2. 前端同学除了自己负责B 端开发业务,也要能够胜任微信小程序各平台的开发工作 基于第一点,我愉快的玩起了 flutter 。

进入Flutter篇

工欲善其事,必先利其器。按照Flutter 开发过程主要是依赖 aar包开发,和依赖源码开发,官方链接:docs.flutter.dev/add-to-app/... 通过照猫画虎的骚操作后,发现根本没有卵用,官方给的文档毕竟玩不转。那么问题来了,你通常时怎么使用 aar 包的?

打包

官方打包

flutter build aar

你想要的打包命令

flutter clean & flutter build aar --build-number 1.1.1 --no-profile --no-release

profile解释: 在 Android 开发中,"profile" 版本通常指的是应用的性能分析版本。这种版本允许开发者在设备上运行应用程序以进行性能分析和调试。这种版本通常会包含更多的日志记录和性能分析工具,用于分析应用在不同设备上的性能表现,并检测可能存在的性能问题。 Flutter 构建过程中的 --no-profile 标志会指示系统不生成这种性能分析版本。这样做是为了加快构建过程并减小生成的输出文件大小。

通过以上命令,你会在"/your_flutter/build/host/outputs/repo"目录下拿到 aar包,如下:

那么,aar包怎么用呢?

aar 包导入推荐策略

策略1:官方推荐导入

配置完成你会发现,flutter 引擎的依赖依赖项一个也拿不到,就是下面这个几个货:

你肯定觉得卧槽了:是的,兵法上没写,但是打仗的时候要用。马谡就是这么丢街亭的~ 那《兵法》上咋说的呢?

兵法上说,你按照我说的运行完 flutter build aar 命令,配置你的 project 依赖绝对没问题。看看这句无耻的话:

说无耻,其实有些过分了。毕竟人家写兵法的人言简意深来着~

First of all,这兵法主要是给资本主义市场使用的

Secondly ,为了迎合中国市场,也告诉你要配置中国镜像

问题就在这里,切换镜像之后,flutter 模块的打包aar的依赖没有被上传到指定的服务端。

墙外:

执行flutter build aar 后,在'/Users/edwin/edwin/MeiTuanEdwin/my_flutter/build/host/outputs/repo文件夹下面会生成aar包和pom文件,后者标示了flutter引擎依赖项,如下:

flutter_debug-1.1.4.pom文件里存放的是依赖项信息:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <!-- This module was also published with a richer model, Gradle metadata,  -->
  <!-- which should be used instead. Do not delete the following line which  -->
  <!-- is to indicate to Gradle or any Gradle module metadata file consumer  -->
  <!-- that they should prefer consuming it instead. -->
  <!-- do_not_remove: published-with-gradle-metadata -->
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example.my_flutter</groupId>
  <artifactId>flutter_debug</artifactId>
  <version>1.1.4</version>
  <packaging>aar</packaging>
  <dependencies>
    <dependency>
      <groupId>io.flutter</groupId>
      <artifactId>flutter_embedding_debug</artifactId>
      <version>1.0.0-9064459a8b0dcd32877107f6002cc429a71659d1</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>io.flutter</groupId>
      <artifactId>armeabi_v7a_debug</artifactId>
      <version>1.0.0-9064459a8b0dcd32877107f6002cc429a71659d1</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>io.flutter</groupId>
      <artifactId>arm64_v8a_debug</artifactId>
      <version>1.0.0-9064459a8b0dcd32877107f6002cc429a71659d1</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>io.flutter</groupId>
      <artifactId>x86_64_debug</artifactId>
      <version>1.0.0-9064459a8b0dcd32877107f6002cc429a71659d1</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>io.flutter</groupId>
      <artifactId>x86_debug</artifactId>
      <version>1.0.0-9064459a8b0dcd32877107f6002cc429a71659d1</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
</project>

这些都是Gradle发布的 Meta data,最终被发布到storage.googleapis.com 这个服务端,点击下面的链接试试

storage.googleapis.com/download.fl...

上面提到 flutter 库在编译时就已经做了架构适配和引擎依赖构建,原则上这些元数据都应该被发布到服务端,这样才能通过 dependencies 配置依赖获取相应的aar包,事实上根本没有我们想要下载的依赖包,搜索唯一标记 9064459a8b0dcd32877107f6002cc429a71659d1(以编译生成POM里的version为准) 无法得到想要的依赖。

墙内

flutter相关配置如下:

根据官方指导,domain切换后,源文件会发布到 storage.flutter-io.cn/download.fl... 点击进入这个站点,同样找不到对应的POM文件中生成的依赖信息

找不到的原因是,flutter build aar 的发布产物是本地的 repo 库,人家压根没有上传到服务的,发布个毛线。 嗨,

兵法坑死人~

尽信书不如无书啊!

这就造成了我们在使用aar包时,需要逐个引入POM文件中的依赖项,进行引擎依赖配置:

arduino 复制代码
    implementation 'io.flutter:flutter_embedding_debug:1.0.0-9064459a8b0dcd32877107f6002cc429a71659d1'
    implementation 'io.flutter:armeabi_v7a_debug:1.0.0-9064459a8b0dcd32877107f6002cc429a71659d1'
    implementation 'io.flutter:arm64_v8a_debug:1.0.0-9064459a8b0dcd32877107f6002cc429a71659d1'
    implementation 'io.flutter:x86_64_debug:1.0.0-9064459a8b0dcd32877107f6002cc429a71659d1'
    implementation 'io.flutter:x86_debug:1.0.0-9064459a8b0dcd32877107f6002cc429a71659d1'

我自然是不愿意搞这么多配置的,关于aar包源数据发布上传这部分,我耗了很久反复阅读官方文档,并且在网上查询相关资料,得到如下提示:

1)Stack Overflow 给出的策略,是直接更改flutter SDK包里的 aar_init_script.gradle文件,很多大厂也是这么干的 stackoverflow.com/questions/7...

2)华为开发者联盟

分析的很透彻,最后希望你通过脚本实现,一言以蔽之:通过执行 flutter build aar -v 查看flutter build aar 的执行秘密,文章在这儿

blog.csdn.net/Ever69/arti...

我还真就这么干了,好奇心害死猫。咳~~~

3)官方指导文档真坑爹

在我们运行flutter build aar时,其实他是借助于.android项目配置Gradle或者Gradle Wrapper能力,其核心工作在 flutter-SDK-Dir/flutter/packages/flutter_tools/gradle/aar_init_script.gradle 文件中完成aar发布工作,具体逻辑如下:

aar文档相关源数据都在本地repo库里,所以通过Maven依赖的远程服务是断断无法获取到的。

综上所述,官方文档给出的关于aar的配置,是指导开发人员使用发布到本地的aar文件的方法,而且还缺少引擎依赖的相关指导

为了能够简化使用flutter模块 aar包的引入流程,我们需要有自己的Maven私服,并且将flutter项目的aar包及相关metadata文件,发布到云端进行更新和迭代。这里选择阿里云效,完成这个任务

发布AAR包到Maven私服

云端选择阿里云效 ,打开阿里云的云效Packages官网:

packages.aliyun.com/maven

你会发现阿里云提供的免费私有Maven库服务,并且介绍了Gradle 7.0前后如何发布项目文档至私有仓库。

上面提到了,发布到私服有两个方案:

1)更改aar_init_script.gradle文件

更改方便,但是侵入性比较强,而且每个人都要更改自己的SDK包。哪天编译脚本升级了,大家一起抓瞎~

2)在flutter项目中直接发布

As we all know,aar包之所以能够通过Gradle编译成功,是依赖其my_flutter/.android项目,该项目中的有一个Flutter库Module,是执行flutter build aar时自动生成的。通过flutter build aar -v 可以很清晰的看到,编译过程中,flutter 相关产物都放到了.android/Flutter模块的build文件夹下,看编译日志:

bash 复制代码
Starting process 'command '/Users/edwin/Library/flutter/bin/flutter''. 
Working directory: /Users/edwin/edwin/MeiTuanEdwin/my_flutter 
Command: /Users/edwin/Library/flutter/bin/flutter  #Flutter 工具的路径
 --verbose #启用详细输出,以便查看更多构建信息
 assemble #执行组装操作,即构建过程
 --no-version-check #禁用版本检查,Flutter 不会检查是否有新版本
 --depfile /Users/edwin/edwin/MeiTuanEdwin/my_flutter/.android/Flutter/build/intermediates/flutter/debug/flutter_build.d #指定依赖文件的路径
 --output /Users/edwin/edwin/MeiTuanEdwin/my_flutter/.android/Flutter/build/intermediates/flutter/debug #指定输出目录的路径
 -dTargetFile=lib/main.dart #指定入口文件的路径
 -dTargetPlatform=android #指定目标平台为 Android
 -dBuildMode=debug #指定构建模式为调试模式
 -dTrackWidgetCreation=true #启用 Flutter 的控件创建跟踪
 debug_android_application

看明白了吧,该命令的目标是构建一个用于调试的 Android 版本的 Flutter 应用程序,output命令指定了输出目录。我们看看output文件夹所在的环境:

这说明我们的 Flutter 模块就是用来生成 aar 包的对应资源而建立的,那我们完全可以在 Flutter 项目的 build.gradle 文件中添加一个 task 用于发布 aar 包,发布任务通过publish.gradle文件完成。实现如下:

bash 复制代码
// publish flutter-debug.aar
//plugins {
//    id 'java'
//    // Gradle 7.0+ 版本
//    id 'maven-publish'
//}

group 'com.example.my_flutter'
version '1.1.4'
def artifactIdStr = 'flutter_debug'

apply plugin: 'maven-publish'
task comps { // 让我们看看它有哪些组件:
    afterEvaluate {
        println("Components: " + components*.name)  // 代码 1
    }
}
def directoryToPublish = "$rootDir.parentFile/build/host/outputs/repo/com/example/my_flutter/flutter_debug/$project.version"
afterEvaluate { // 代码 2
    publishing {
        publications {
            flutter(MavenPublication) { // 代码 3
                from components.debug
                println("groupId = " + groupId + ",artifactId = " + artifactIdStr + ",version = " + version + ",buildDir = " + buildDir)
                groupId = "$project.group" // 代码 4
                artifactId = artifactIdStr
                version = "$project.version"

//                artifact "$buildDir/outputs/aar/flutter-debug.aar" //aar artifact you want to publish
                // artifact "$directoryToPublish/${artifactIdStr}-${project.version}.aar"
                // artifact "$directoryToPublish/${artifactIdStr}-${project.version}.module"
                // artifact "$directoryToPublish/${artifactIdStr}-${project.version}.pom"

//                artifact("$directoryToPublish/${artifactIdStr}-${project.version}.aar") /*{
//                    classifier 'aar' // 使用 'aar' 作为分类器
//                }*/
//                artifact("$directoryToPublish/${artifactIdStr}-${project.version}.module") /*{
//                    classifier 'module' // 使用 'module' 作为分类器
//                }*/
//                artifact("$directoryToPublish/${artifactIdStr}-${project.version}.pom")/* {
//                    classifier 'pom' // 使用 'pom' 作为分类器
//                    extension 'pom' // 设置为 'pom' 类型
//                }*/
            }
        }
        repositories { // 代码 5
//            maven {
//                url = 'https://packages.aliyun.com/maven/repository/2443959-release-V0Shv7/'
//                credentials {
//                    username = '##############'
//                    password = '*****************'
//                }
//            }
            maven {
                url = 'https://packages.aliyun.com/maven/repository/2443959-snapshot-5T0GnA/'
                credentials {
                    username = '###############'
                    password = '******************'
                }
            }
        }
    }
}

代码 1 处的 components*.name 用于确认当前Gradle 会构建那些组件用于发布,输出结果如下:

从打印结果来看,可以构建 debug、profile、release 三种组件及其组合组件all,我们使用 debug 版本

代码 2 处,评估之后开始进入发布工作,依赖 maven-publish 插件进行发布,gradle 要配置成 7.5 以上的版本

代码 3 处,定义了一个 MavenPublication,命名为 "flutter"。from components.debug 表示将 MavenPublication 的内容从 Gradle 构建中的 components.debug 组件中获取。这里的MavenPublication包含了该组件的输出,例如 AAR 文件、POM 文件等。这是为了将 Flutter 模块构建的输出发布到 Maven 仓库

代码 4 处配置了 GVA 用于唯一标识当前AAR 包,方便其他项目通过 Maven 或gradle 进行依赖,如下:

当然,现在依赖还是无法完成,因为aar 包要发布到远程 Maven,

代码 5 处就是用于配置远程服务地址的,我们选用的是阿里云效服务,用起来也算是麻烦+坑爹。

阿里云效:packages.aliyun.com/maven

如果玩不来,这里还有篇文章可以参考一如何把 library 发布到阿里云效:

shawlaw.github.io/Fragmentary...

发布任务编码完成~

然后在 Flutter 模块的 build.gradle 中 apply publish.gradle代码如下:

csharp 复制代码
def currentTask = gradle.startParameter.taskNames.join(" ")
println("currentTask =" + currentTask)
if (!currentTask.contains("Aar")) {
    apply from: "../../publish.gradle"
}

这段代码的意思就是说,在 Flutter 项目编译过程中,准确的说是评估的时候,把发布任务引入,等compile完成后执行发布任务。

最后,咱们再执行一遍 flutter build aar 指令,发现.android/Flutter/build/目录下没有生成 flutter 发布文件夹,

逗我呢?!

老铁,这里还得执行一遍 ./gradlew publish 命令,执行发布任务,现在可以愉快的看到如下效果了:

那么,现在赶快在另一个项目里试一下配置依赖吧。 首先在需要使用 aar包的项目根目录下 root.gradle 中配置 Maven 库,参考如下:

其次在需要使用 aar包的 module 中,配置 AVG 依赖如下:

现在同步代码,等待依赖完成。flutter 引擎的五个依赖如期而至:

Shell脚本一键打包发布

等等老哥~ 道理我都听明白了,上面的代码还是要手动更改 Flutter 项目中的 build.gradle 文件啊。更要命的是,如果执行 flutter clean 清理一遍编译工程,刚刚的文档不都白瞎了么。你这是让我哭吗?

我说老兄你等等,这不是上脚本呢嘛~

  1. 首先,我们把发布文件 publish.gradle 移动到 my_flutter 项目根目录下,并在根目录下创建一个shell 脚本文件 apply_patch.sh,如下:

  2. 然后,将 publish.gradle 导入到 Flutter 项目中的诉求未变,通过 shell 脚本实现如下:

shell 复制代码
    # 获取当前脚本所在目录的绝对路径
    SCRIPT_DIR=$(dirname "$(readlink -f "$0")")

    # 切换到Flutter项目的根目录
    cd "$SCRIPT_DIR/.android/Flutter" || exit
    # 在Flutter/build.gradle文件中执行命令:
    # 1)在android配置项前插入 apply 发布应用
    # 2)-i '.bak',备份原始文件
    sed -i '.bak' '/^android {/i\
    def currentTask = gradle.startParameter.taskNames.join(" ") \
    println("currentTask =" + currentTask)\
    if (!currentTask.contains("Aar")) {\
        apply from: "../../publish.gradle"\
    }' "$SCRIPT_DIR/.android/Flutter/build.gradle"

通过shell脚本将发布任务添加到 .android/Flutter 项目中,脚本的含义都写在了注释里。

  1. 插入完成后,还要进行发布工作,仍然通过脚本完成,如下:
bash 复制代码
# 切换到 ./android 目录下执行发布任务
cd "$SCRIPT_DIR/.android" || exit
./gradlew publish --info > publish.log 2>&1
if [ $? -ne 0 ]; then
    echo "发布任务失败!查看 publish.log 获取更多信息。"
    exit 1
fi

这里把发布日志都打印在了 publish.log 文档里,如果发布失败./gradlew publish --info 会提供详细的失败信息。

  1. 版本控制,aar 包是有自己的版本号的,比如 1.1.4。为了统一控制 version,不要手动更改gradle 文件,我们引入如下脚本:
bash 复制代码
# 替换 publish.gradle 中的版本号
OLD_VERSION=$(grep -o "version '[0-9]\+\.[0-9]\+\.[0-9]\+'" "$SCRIPT_DIR/publish.gradle" | head -n 1)
if [ -z "$OLD_VERSION" ]; then
  echo "无法获取旧版本号!"
  exit 1
fi

sed -i'.bak' "s/$OLD_VERSION/version '$BUILD_NUMBER'/" "$SCRIPT_DIR/publish.gradle" || { echo "版本号替换失败!"; exit 1; }
  1. 合并所有脚本,得到完整的发布脚本apply_patch.sh,脚本如下

    作为一个广告引流专业户,还是详见 github 代码库吧~

  2. 执行 ./apply_patch.sh 脚本

到这里整个一键打包发布工作就完成了

FAQ

1.local.properties 类似文件属性配置时,不要加引号,不然在 gradle 文件中获取到的值要处理

  1. gradle 的版本要配置成 7.5,太高版本不见得稳定,而且低版本调用他的时候存在融合问题:

参考文献

  1. GitHub库地址 github.com/hongyi0609/...
  2. my_flutter 库地址 github.com/hongyi0609/...
  3. flutter 国内镜像 flutter.cn/community/c...
  4. aar 服务端 storage.googleapis.com/download.fl...
  5. 华为开发者联盟分析 blog.csdn.net/Ever69/arti...
  6. 私服配置 stackoverflow.com/questions/7...
  7. 阿里云效:packages.aliyun.com/maven
相关推荐
Jinkey6 小时前
FlutterBasic - GetBuilder、Obx、GetX<Controller>、GetxController 有啥区别
android·flutter·ios
Summer不秃10 小时前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰11 小时前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
sunly_11 小时前
Flutter:AnimatedSwitcher当子元素改变时,触发动画
flutter
AiFlutter11 小时前
Flutter封装Coap
flutter
旭日猎鹰16 小时前
Flutter踩坑记录(三)-- 更改入口执行文件
flutter
旭日猎鹰16 小时前
Flutter踩坑记录(一)debug运行生成的项目,不能手动点击运行
flutter
️ 邪神16 小时前
【Android、IOS、Flutter、鸿蒙、ReactNative 】自定义View
flutter·ios·鸿蒙·reactnative·anroid
比格丽巴格丽抱1 天前
flutter项目苹果编译运行打包上线
flutter·ios
SoaringHeart1 天前
Flutter进阶:基于 MLKit 的 OCR 文字识别
前端·flutter