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
相关推荐
Ranye1233 小时前
从 JS 到 Dart:语法基础
javascript·flutter·dart
我要最优解10 小时前
关于在mac中配置Java系统环境变量
java·flutter·macos
江上清风山间明月2 天前
Flutter开发的应用页面非常多时如何高效管理路由
android·flutter·路由·页面管理·routes·ongenerateroute
Zsnoin能2 天前
flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题
flutter
早起的年轻人2 天前
Flutter CupertinoNavigationBar iOS 风格导航栏的组件
flutter·ios
HappyAcmen2 天前
关于Flutter前端面试题及其答案解析
前端·flutter
coooliang3 天前
Flutter 中的单例模式
javascript·flutter·单例模式
coooliang3 天前
Flutter项目中设置安卓启动页
android·flutter
JIngles1233 天前
flutter将utf-8编码的字节序列转换为中英文字符串
java·javascript·flutter
B.-3 天前
在 Flutter 中实现文件读写
开发语言·学习·flutter·android studio·xcode