环境:Arch Linux on WSL2,Flutter 3.47.9,Android SDK 36,16 核 / 23G 内存。 现象:
flutter build apk动辄十几分钟,甚至卡住不动。本文把「Arch Linux 下 Flutter 环境搭建」和「构建慢的排查过程」合在一起写,既是一份安装记录,也是一份性能排障笔记。 环境搭建部分参考了 Archlinux 下 Flutter 开发环境的搭建。
0. 先给结论
构建慢不是单一原因,而是 5 个问题叠加:
| # | 问题 | 典型表现 | 修复后 |
|---|---|---|---|
| 1 | JDK 27 与 Gradle 8.14 不兼容 | Unsupported class file major version 71 | 固定 JDK 21 |
| 2 | pub.flutter-io.cn 实际不可用 | pub get 几分钟无输出 | 换交大镜像,68s 完成 |
| 3 | ~/.gradle 为空 + Gradle 发行包镜像慢 | 每次冷启动下载 200MB+ | 预置发行包 + 华为云镜像 |
| 4 | 缺 build-tools;35.0.0 / platforms;android-35 | AGP 自动安装失败 | 手动从国内镜像安装 |
| 5 | 插件构建时从 GitHub 下载 libsmb2.so | 卡 180 秒后失败 | 一次性国内加速拉取并缓存 |
最终效果:
| 场景 | 修复前 | 修复后 |
|---|---|---|
flutter pub get(首次) | 超时 / 数分钟 | 68 s |
| 首次完整 release 构建 | 8~15 分钟或直接失败 | 3 min 32 s |
| 无改动二次构建 | 分钟级 | 12.5 s |
| 补完 SMB 原生库后二次构建 | — | 21 s |
1. 基础环境安装(Arch Linux)
参考文章的安装方式是用 yay 安装系统包:
yay -S flutter
yay -S android-sdk
yay -S dart
yay -S android-tools
yay -S android-sdk-platform-tools
yay -S android-sdk-cmdline-tools-latest
yay -S android-sdk-build-tools-31.0.0
yay -S android-platform-31
几点补充:
- 不一定要锁死
31.0.0,但要保证 build-tools 与 platform 版本配套,否则会出现“找不到 SDK”的报错。 - 现在 Flutter 3.47 默认要求更高的 compileSdk / build-tools,建议至少装 35 和 36:
yay -S android-sdk-build-tools-35 android-platform-35
yay -S android-sdk-build-tools-36 android-platform-36
- Flutter 和 Android SDK 的目录权限要归当前用户,否则后面 AGP 自动安装组件会失败(这是本文第 4 个坑的根源):
sudo chown -R "$USER:$USER" /opt/flutter
sudo chown -R "$USER:$USER" /opt/android-sdk
sudo chown -R "$USER:$USER" /opt/dart-sdk
配置 SDK 路径并检查:
flutter config --android-sdk /opt/android-sdk
flutter doctor -v
如果需要 Flutter Web,再安装 Chrome:
yay -S google-chrome
sudo ln -s /usr/bin/google-chrome-stable /usr/bin/google-chrome
2. 国内镜像:把 ~/.env 一次配好
~/.env 由 .bashrc / .zshrc source,集中管理最省心:
# ===== 国内镜像 & 构建环境 =====
# Pub:pub.flutter-io.cn 已不可用,改用上海交大镜像
export PUB_HOSTED_URL="https://mirror.sjtu.edu.cn/dart-pub"
export FLUTTER_STORAGE_BASE_URL="https://storage.flutter-io.cn"
export FLUTTER_GIT_URL="https://mirrors.cernet.edu.cn/flutter-sdk.git"
# JDK 21:Gradle 8.14 不支持 JDK 27
export JAVA_HOME="$HOME/.local/jdk21"
export PATH="/opt/flutter/bin:$JAVA_HOME/bin:$PATH"
export ANDROID_HOME="/opt/android-sdk"
export ANDROID_SDK_ROOT="$ANDROID_HOME"
实测镜像速度(同一网络):
pub.flutter-io.cn:下载 tar 包直接超时- 交大
mirror.sjtu.edu.cn/dart-pub:可用,pub get68 秒 - 腾讯
mirrors.cloud.tencent.com/dart-pub:可用但更慢 - 清华 / 中科大:目录已下线或 404
小贴士:国内 pub 镜像返回的
archive_url仍可能指向storage.flutter-io.cn,所以镜像只解决“元数据解析”,包体下载速度取决于 storage 节点。
3. JDK:Gradle 8.14 不认识 JDK 27
系统默认 Java 是 27,直接构建报错:
BUG! exception in phase 'semantic analysis' in source unit '_BuildScript_'
Unsupported class file major version 71
原因:Gradle 8.14 最高只支持 JDK 24,而 JDK 27 的 class 文件版本是 71。
做法是单独装一个 JDK 21(不必动系统默认 Java):
mkdir -p ~/.local/jdk21
curl -L -o /tmp/jdk21.tar.gz \
https://mirrors.huaweicloud.com/openjdk/21.0.2/openjdk-21.0.2_linux-x64_bin.tar.gz
tar -xzf /tmp/jdk21.tar.gz -C ~/.local/jdk21 --strip-components=1
~/.local/jdk21/bin/java -version
然后让 JAVA_HOME 指向它。flutter doctor -v 里应看到:
Java binary at: /home/xero/.local/jdk21/bin/java
Java version OpenJDK 21.0.2
4. Gradle:发行包和依赖仓库都要走国内
4.1 Gradle 发行包
android/gradle/wrapper/gradle-wrapper.properties 默认可能指向国内某镜像,但速度差异巨大。实测同一个 gradle-8.14-all.zip(214MB):
| 镜像 | 速度 | 预计耗时 |
|---|---|---|
| 腾讯云 | ~0.28 MB/s | ~13 分钟 |
| 华为云 | ~3.4 MB/s | ~1 分钟 |
建议把 distributionUrl 换成华为云:
distributionUrl=https://mirrors.huaweicloud.com/gradle/gradle-8.14-all.zip
如果暂时不想改仓库文件,也可以把发行包预置到 wrapper 缓存目录(目录名是 URL 的 MD5 的 base36):
~/.gradle/wrapper/dists/gradle-8.14-all/<hash>/gradle-8.14-all.zip
4.2 Maven 依赖:用 init 脚本全局加镜像
Flutter 项目的依赖来自三类仓库:pluginManagement(插件)、项目 repositories(依赖)、included build(Flutter 自带的 flutter_tools/gradle)。只改 build.gradle 往往漏掉插件解析。
创建 ~/.gradle/init.gradle:
def mirrorUrls = [
'https://mirrors.huaweicloud.com/repository/maven/',
'https://maven.aliyun.com/repository/google/',
'https://maven.aliyun.com/repository/public/',
]
// 关键:pluginManagement 的仓库必须在 settings 求值前生效
beforeSettings { settings ->
settings.pluginManagement.repositories {
mirrorUrls.each { u -> maven { url = u } }
gradlePluginPortal()
}
// included build 使用 PREFER_SETTINGS,需要 settings 级仓库
settings.dependencyResolutionManagement.repositories {
mirrorUrls.each { u -> maven { url = u } }
}
}
// 只给根构建注入 project 级仓库;
// included build 若被注入 project 仓库会报
// "prefer settings repositories over project repositories"
if (gradle.parent == null) {
allprojects {
repositories {
mirrorUrls.each { u -> maven { url = u } }
}
}
}
这里有两个容易踩的坑:
- 只用
allprojects不够,插件解析在 settings 阶段就发生了,必须用beforeSettings。 - 无脑给所有 build 注入 project 仓库,会让 Flutter 的 included build 直接报错,所以要判断
gradle.parent == null。
修复后日志里境外 Maven 下载次数为 0,华为云命中 140 次。
5. Android SDK 组件与协议
5.1 缺少 build-tools 35
构建时会报:
Failed to install the following SDK components:
build-tools;35.0.0 Android SDK Build-Tools 35
即使协议已接受,AGP 自动安装仍可能失败。常见原因是 SDK 目录属主是 root:
sudo chown -R "$USER:$USER" /opt/android-sdk
本文环境没有免密 sudo,只能从国内镜像手动安装。组件清单在腾讯镜像的 repository2-3.xml 里:
curl -sL -o /tmp/repo23.xml \
https://mirrors.cloud.tencent.com/AndroidSDK/repository2-3.xml
其中 build-tools;35.0.0 对应 build-tools_r35_linux.zip,platforms;android-35 对应 platform-35_r02.zip:
curl -L -o /tmp/build-tools_r35_linux.zip \
https://mirrors.cloud.tencent.com/AndroidSDK/build-tools_r35_linux.zip
unzip -q /tmp/build-tools_r35_linux.zip -d /tmp/bt35
mv /tmp/bt35/android-15 /opt/android-sdk/build-tools/35.0.0
如果
build-tools/目录是 root 所有,又无法 sudo,可以临时改名原目录、新建目录并给旧版本建符号链接。能用chown就不要用这个办法,AGP 会提示 “inconsistent location”。
5.2 新版 cmdline-tools 不再支持 --licenses
新版 sdkmanager 会提示:
Warning: The --licenses option is no longer needed.
如果 AGP 仍然检查协议文件,可以手动写入 $ANDROID_HOME/licenses/:
mkdir -p /opt/android-sdk/licenses
cat > /opt/android-sdk/licenses/android-sdk-license <<'EOF'
24333f8a63b6825ea9c5514f83c2829b004d1fee
84831b9409646a918e30573bab4c9c91346d8abd
d975f751698a77b662f1254ddbeed3901e976f5a
EOF
6. 最隐蔽的坑:插件在构建时下载 GitHub Release
dart_smb2 0.0.8 的 android/build.gradle.kts 里有这样一段:
if (!isValid) {
val url = "${SMB2_BASE_URL}/${filename}"
println("[dart_smb2] Downloading libsmb2.so for ${abi} from ${url}")
URL(url).openStream().use { input ->
FileOutputStream(targetFile).use { output -> input.copyTo(output) }
}
}
问题在于:
- 下载发生在 Gradle 构建阶段,不是
pub get阶段,所以换 pub 镜像没用; URL.openStream()没有设置超时,GitHub 不可达时会一直等到 TCP 超时;- 本次实测整整卡了 180 秒 后报
Unexpected end of file from server,构建失败。
输出类似:
> Task :dart_smb2:downloadSmb2Libraries FAILED
[dart_smb2] Failed to download libsmb2.so for arm64-v8a: Unexpected end of file from server
解决办法:一次性拉取并放进 pub 缓存
脚本会用 SHA-256 校验文件,只要文件存在且哈希匹配,就不会再联网。三个 ABI 的哈希在插件源码里写死,可以从日志或源码拿到。
使用国内 GitHub 加速镜像(示例,按需替换):
BASE="https://gh-proxy.com/https://github.com/ales-drnz/dart_smb2/releases/download/libsmb2-r5"
P="$HOME/.pub-cache/hosted/mirror.sjtu.edu.cn%47dart-pub%47/dart_smb2-0.0.8"
for abi in arm64-v8a armeabi-v7a x86_64; do
f="libsmb2_android-$abi.so"
curl -L -o "/tmp/$f" "$BASE/$f"
sha256sum "/tmp/$f" # 与插件里的哈希对照
mkdir -p "$P/android/src/main/jniLibs/$abi"
cp "/tmp/$f" "$P/android/src/main/jniLibs/$abi/libsmb2.so"
done
之后构建会直接复用缓存,不再访问 GitHub。确认 APK 中包含原生库:
unzip -l build/app/outputs/flutter-apk/app-release.apk | grep libsmb2
# lib/arm64-v8a/libsmb2.so
# lib/armeabi-v7a/libsmb2.so
# lib/x86_64/libsmb2.so
如果以后升级插件版本,哈希和 release 版本都会变,需要重新拉取。
dart_smb20.3.0 改用了 native assets hook,也支持通过hooks.user_defines.dart_smb2.libsmb2指向本地文件。
7. 其他值得做的优化
7.1 只编需要的 ABI
flutter build apk 默认编 3 个 ABI(arm64-v8a / armeabi-v7a / x86_64),AOT 编译和打包时间约为单 ABI 的 2~3 倍。
本项目 Makefile 已经写好了:
build:
flutter build apk --release --target-platform android-arm64
平时直接用 make build。
7.2 打开 Gradle 并行和缓存
android/gradle.properties:
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:+HeapDumpOnOutOfMemoryError
配置缓存(org.gradle.configuration-cache=true)对 Flutter 插件生态兼容性一般,建议先不开。
7.3 版本别太旧
Flutter 3.47 已经提示 Gradle 8.14 / AGP 8.11 / Kotlin 2.2.20 即将停止支持。升级前先确认插件兼容性,不要一次跳太多。
8. 快速检查清单
# 1. JDK 必须是 17~24(本机用 21)
java -version
flutter doctor -v | grep -i java
# 2. pub 镜像可用
curl -sI "$PUB_HOSTED_URL" | head -1
# 3. Gradle 发行包是否已缓存
ls ~/.gradle/wrapper/dists/
# 4. Maven 是否走国内镜像(构建日志里不应大量出现 dl.google.com / repo.maven.apache.org)
flutter build apk --verbose 2>&1 | grep "Downloading https://"
# 5. SDK 组件是否齐全
ls /opt/android-sdk/build-tools /opt/android-sdk/platforms
# 6. 单独测一次增量构建耗时
time flutter build apk --release --target-platform android-arm64
9. 经验总结
- 先看错误,再谈优化。 这次一半的时间不是“慢”,而是“悄悄失败”:JDK 版本、镜像失效、SDK 组件缺失、GitHub 超时。
- 把 SDK / Flutter 目录 chown 给当前用户。 否则 AGP 自动安装组件会以“看起来像网络问题”的方式失败。
- 国内镜像要分层配置。 Pub、Flutter artifacts、Gradle wrapper、Maven 依赖、GitHub Release 资源是四套不同的东西,只配一个不够。
- Gradle 的 init 脚本比改项目更通用,但要注意 settings 阶段和 included build 的仓库模式。
- 任何构建期下载都要有超时和离线回退。 插件里的
URL.openStream()是本次最坑的地方。 - 缓存是性能的核心。 冷构建 3.5 分钟,增量构建 12~21 秒,差了一个数量级;不要把冷构建的耗时当成日常开发体验。
参考
- Archlinux 下 Flutter 开发环境的搭建 — Less Bug
- Flutter 官方 Android 构建文档
- Gradle 兼容性矩阵(Java / Kotlin / AGP 版本)