1. 引言
😶 在整 Flutter 之前,小组的 Android打包工作 都是交由一台闲置 老旧的Mac mini (i5 8G) 来完成的:在 MacOS 上部署 Jenkins → APK构建+上传+钉钉群通知 。😐 后面搞 混合开发 ,把 Flutter 也整进来了,我在前面《Window VM虚拟机🧱 安装 MacOS🍎》安装的 MacOS虚拟机 上把旧的打包脚本修改调试完跑通后,准备搬到打包机上试试。😑 结果直接卡在 配置Flutter开发环境 这一步上:
执行 flutter doctor 提示 更新XCode ,打开 AppStore 点击 安装XCode,提示:不能将Xcode安装在xxx上,因为需要macOS xxx 或更高版本。
😳 问题是我点击软件更新,提示:您的Mac运行的已是 最新版本 ??? 尝试《安装低版本的Xcode》 futter doctor 依旧让我更新 XCode。🙃 唉,老mac mi 打包 Flutter APK 无望,只能跟 iOS 童鞋挤一挤,用一个打包机了,适配它们的环境改了下打包脚本成功打包。
😑 正当我以为后面可以一劳永逸时,某天我们的打包脚本直接G了,一直编译失败。问他们干嘛了,说只是挪了下文件路径。花了不少时间排查,结果发现是改了环境变量 & 代理问题。
😐 想想还是自己整个备用打包机兜底,旧的macOS 不行,那就装个 Ubuntu ,比起 Windows 所需的硬件要求更低,还可以拿来编 Android源码。折腾过程踩了不少坑,简单记录下...
2. 系统安装
2.1. 下载系统镜像
这里选择的是经典版本 Ubuntu 22.04 ,打开 官网:Ubuntu 22.04.5 LTS (Jammy Jellyfish) 下载下述镜像:
2.2. 制作启动盘
用到的烧录工具 balenaEthcer,旧mac mini 是i5的,直接下x64的安装包:
安装后打开,选 iso镜像 ,选作为 启动盘 的U盘,然后开始烧录:
等待烧录完毕,启动电脑,按住ALT键不放 ,然后会进入阴道页面,选择「EFI boot」:
选择「try or install ubuntu 」,进入 Ubuntu 系统安装引导页,语言-English (US)、其它默认选,除了「Install Ubuntu 」要注意下,这里会抹除所有硬盘数据,如果是装双系统不要选这个,我这里是重装,所以直接选。然后就是等待漫长的安装,安装完会提示重启,让你拔掉U盘,拔完重启后就是Ubuntu系统啦~
3. 软件安装
3.1. 远程桌面
😀 新系统啥都没,直接装个远程桌面,我可以在自己的电脑上边捣鼓边搜索,这里选择的远程桌面工具是 ToDesk,选它的原因:个人免费+有Linux版本,安装方法:Debian/Ubuntu/Mint
bash
# 安装
sudo apt-get install ./todesk-v4.7.2.0-amd64.deb
# 启动
todesk
😶 安装启动后,Windows电脑尝试连接,遇到问题:卡100%进度 + 小选付框打开就消失 ,解决方法:删掉 /etc/gdm3/custom.conf 文件中 WaylandEnable=false 前的 # 号,然后重启,具体过程可以参考:《Todesk远程连接Ubuntu卡100%,以及小窗口打不开》
3.2. 科学上网
😶 有科学上网就能 少配或者不用配各种镜像源 ,V2xx和Claxx都相继跑路没更新了,虽然有热心网友提供了备份下载,但在Ubuntu上配置起来也比较繁琐,这里用到另一个后起之秀:clash-verge-rev,下载 Debian系64位的deb文件 ,执行 apt ./xxx.deb 进行安装:
界面和Claxx基本一直,添加下 订阅 ,开下 系统代理 选项:
接着设置下 全局代理 ,点击系统 设置 → 网络 ,找到 网络代理,做下配置:
可以浏览器访问下Google看代理是否生效~
3.3. 搜狗输入法
照着《Ubuntu搜狗输入法安装指南》走一波就好了~
3.4. Chrome浏览器
终端走一波下述命令直接安装:
bash
sudo apt update -y
sudo apt install wget -y
wget "https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb" -O google.deb
sudo dpkg -i google.deb
3.5. zsh 美化终端
环境安装、主题配置和创建安装可以参考这篇文章《zsh 安装与配置,使用 oh-my-zsh 美化终端》,基本的软件就安装到这,接着搞下 Flutter 开发环境。
4. Flutter 开发环境搭建
下载安装下述文件:
- OpenJDK-17 :执行 sudo apt install openjdk-17-jdk 命令安装
- Flutter SDK :官网下载
- Android Studio :官网下载,安装 Dart 和 Flutter 插件
为了方便管理,我在 home 目录下新建了一个 opt 文件夹来存放上述文件的解压内容,执行「sudo gedit ~/.bashrc」在文件的尾部添加下述环境变量:
bash
JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
ANDROID_SDK_HOME=~/opt/android-sdk
STUDIO_HOME=~/opt/android-studio
FLUTTER_HOME=~/opt/flutter
export CLASSPATH=.:$JAVA_HOME/lib:$CLASSPATH
export PATH=$JAVA_HOME/bin:$ANDROID_SDK_HOME/platform-tools:$ANDROID_SDK_HOME/build-tools:$ANDROID_SDK_HOME/cmdline-tools/latest/bin:$STUDIO_HOME/bin:$FLUTTER_HOME/bin:$PATH
PUB_HOSTED_URL=https://pub.flutter-io.cn
FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
运行 source ~/.bashrc 让配置生效,键入 flutter --version ,如果不是提示命令找不到说明配置成功,接着执行 flutter doctor ,缺啥装啥。比如这个 Linux toolchain ,复制e.g里的命令安装对应的库就好,如:apt install clang:
Android toolchain 的:
第一个 cmdline-tools 组件找不到,可以命令安装,也可以直接打开 Android Studio:
找到 Android SDK - SDK Tools - Android SDK Command-line Tools 进行安装:
第二个 license 授权的问题,复制执行下「flutter doctor --licenses 」命令,无脑y回车,最后运行 ****「flutter doctor」
没提示错误的话,Flutter 开发环境 就搭建好了,保险起见,可以新开一个项目,然后运行起来。在通过 ./studio.sh 命令启动Android Studio时碰到下述警告:
改用「bin/studio」启动就不会有中文问题了。
5. Jenkins 打包机部署
😶 大体过程是按照《Ubuntu 22.04 / 20.04 上安装 Jenkins》这篇文章来的~
5.1. 安装 Jenkins
官网《安装Jenkins》给出的安装命令:
bash
wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list'
sudo apt-get update
sudo apt-get install jenkins
执行第二句命令时有 apt-key deprecated 警告,可以不用理,有强迫症不想看到可以看下《修复 apt-key deprecated 警告》,执行最后一条命令安装jenkins报错:
The repository 'pkg.jenkins.io/debian-stab... binary/ Release' is not signed.
解决方法:通过官方存储库进行安装
bash
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee \
/usr/share/keyrings/jenkins-keyring.asc > /dev/null
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
/etc/apt/sources.list.d/jenkins.list > /dev/null
sudo apt update
sudo apt install jenkins
安装完直接浏览器打开:http://localhost:8080,第一次访问时会提示输入初始管理员密码,通过下述命令获取:
bash
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
复制粘贴后,等待loading完毕,选择 安装建议的插件,然后等待安装完毕,可能会有部分失败,retry下
接着会提示你创建一个 Admin 用户,输入账号密码后保存继续,最后点击 Start using Jenkins 就可以访问Jenkins啦,接着是把打包sh脚本跑起来遇到的一些问题~
5.2. 遇到的问题
5.2.1. 权限不足
sh 脚本中需要运行某些程序 & 对某些目录下的文件进行操作,报没有权限。执行命令「sudo gedit /lib/systemd/system/jenkins.service」找到下面两行:
将 jenkins 改为 root,执行下述两个命令让更改生效,没生效的话可以reboot重启下系统:
bash
# 重新加载 systemd 管理的所有服务配置文件
sudo systemctl daemon-reload
# 重启 Jenkins 服务
sudo systemctl restart jenkins
5.2.2. rontend_server_dart: No such file
完整报错信息:
erlang
[INFO] Precompiling build script......
Could not find a command named "/home/dbj/opt/flutter/bin/cache/dart-sdk/bin/snapshots/frontend_server.dart.snapshot".
原因:frontend_server_client 和 build_runner 的兼容问题,具体可查阅《Bump frontend_server_client to ^4.0.0》和 《frontend_server_dart: No such file on 3.16.6》,解决方法 →「Flutter 版本降级」,实施步骤:
- 到 Flutter的 Github 仓库 通过 版本Tag 找到要回退的 commit编号。
- 本地命令行cd 到 flutter sdk 的目录,执行 git reset --hard 版本提交编号回退提交。
- 执行 flutter doctor 等待重新下载和编译。
步骤图解可参考:《Flutter SDK 降级到指定的版本方法》
5.2.3. 找不到或无法加载主类 org.gradle.wrapper.GradleWrapperMain
原因:项目里没有 gradle 文件夹,就下图这个:
😶 解法:找一个正常Android项目,把这个文件夹复制过去 (项目根目录,与app文件夹同级)。
5.2.4. gradle-x.x.x.zip failed: timeout
😄 老生常谈的问题了,常规解法有这四种:
① 项目的「gradle.properties」添加下述内容设置代理
bash
systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=1080
systemProp.https.proxyHost=127.0.0.1
systemProp.https.proxyPort=1080
② 手动下载压缩包
打开项目的 gradle/wrapper/gradle-wrapper.properties 文件,找到这一行:
bash
distributionUrl=https://services.gradle.org/distributions/gradle-x.x-bin.zip
复制链接,浏览器打开下载压缩包,然后放到 /var/lib/jenkins/.gradle/wrapper/dists/gradle-x.x-bin/随机字符 文件夹中,比如:
③ 配置本地Gradle
依旧是修改 gradle-wrapper.properties 文件,将 distributionUrl 指向下载zip的本地路径,如:
distributionUrl=file:///home/username/gradle/gradle-8.9-all.zip
④ 项目级别 build.gradle 添加国内镜像源
如题,一般都是用的 阿里云云效Maven,修改示例如下:
bash
allprojects {
repositories {
maven{ url 'https://maven.aliyun.com/repository/public'}
}
}
😏 上述方法基本是要动项目的,其实,只需要给 Jenkins 配置下代理就可以解决,点击 设置-系统 ,找到 HTTP Proxy Configuration,添加下述代理设置:
配置完,测试下能否正常访问谷歌,返回200说明代理生效。
5.2.5. SDK Location not found.
问题描述得很清楚 → 找不到 Android SDK 的路径 ,解法:配置环境ANDROID_SDK_ROOT 环境变量 & 项目 local.properties 指定sdk路径。系统和jenkins环境变量配置是隔离的,这里直接在sh脚本中指定:
bash
export ANDROID_SDK_ROOT="/home/dbj/opt/android-sdk"
5.2.6. module_plugin_loader.gradle 编译失败
打开看了下这行代码:
em... 就是加载flutter项目啊,不应该有问题啊,网上搜了圈,没找到啥资料,最后在《Android build failed due to build.gradle line 59》找到了解决方法,将 gradle 版本改为 7.4:
🙃 所以,就是 gradle版本 的问题...
5.2.7. java.base does not "opens java.io" to unnamed module
Gradle 和 JDK 版本不匹配的问题,要么 升级gradle ,要么 降级jdk,这里选择后者,看了下自己的电脑是jdk 11 (能正常运行项目),通过下述命令安装jdk 11:
sudo apt install openjdk-11-jdk
安装完,输入 java -version ,会发现还是jdk17,此时两个版本的jdk是并存的,需要给项目指定jdk的路径。一种方法是 → 在 gradle.properties 中添加下述代码指定:
org.gradle.java.home=/usr/lib/jvm/java-17-openjdk-amd64
🤷♀️ 可以,但得修改原项目的文件,其实 Jenkins 中就能直接指定了,设置-Tools找到JDK,添加两个版本的JDK配置:
配置完Save保存,接着来到项目设置,就会有jdk选项,选择上面配置的 jdk-11 即可。
😶 有些同学会问,不能通过设置 环境变量 的方式吗?可以,但有 坑 💩,听我娓娓道来。笔者通过下述命令可以看到系统都安装了哪些jdk版本:
输入对应jdk版本的序号,回车切换对应的版本,再 java -version 就是11的版本了:
javac 版本也是这样的切换,😳 然后,后面一次重启后,Jenkins 服务就起不来了:
查看详细错误信息:
搜了下 jenkins.service: Start request repeated too quickly ,发现大都是说 jdk版本问题 → 每个jenkins版本需要的JDK版本都是不一样的,安装JDK版本过高或过低都是不行的。键入「jenkins --version 」查看 Jenkins 的版本:
☹️ 擦,得17-21,怪不得一直报错,切回去,重启下电脑就好了,真的坑 🙃...
5.2.8. 依赖库拉不下来
😳 就突然拉不下来依赖了,以为是代理的问题,切了一堆镜像,遍历子项目添加maven等,搞了一下午都不行:
🙃 结果TM是 jetpack.io 服务挂了,要不是群里有人提到,我根本想不到:
🤡 后面服务恢复了,就好了...
5.2.9. fir-cil 安装
我们用到 fir 这个分发平台,需要安装下 fir-cli,按照文档给出的命令安装:
bash
curl -sSL https://get.rvm.io | bash -s stable
source ~/.rvm/scripts/rvm
rvm install ruby
结果ruby安装失败,在 fir-cli/issues/51 找到了解决方法:
bash
apt-get install ruby-dev
5.2.10. 电脑死机
😑 我还是 高估了老mini的性能(4核8G) ,直接被 gradle 干死机了 (CPU使用率 + 内存耗尽):
😐 同事说 虚拟内存 可以搞多点,硬盘1T呢,我直接搞个64G,依次执行下述命令进行设置:
bash
# 禁用当前的交换文件
sudo swapoff /swapfile
# 删除旧的交换文件
sudo rm /swapfile
# 从/etc/fstab中删除旧的交换文件条目
sudo sed -i '//swapfile/d' /etc/fstab
# 创建一个新的交换文件
sudo fallocate -l 64G /swapfile
# 设置交换文件的权限
sudo chmod 600 /swapfile
# 将文件设置为交换空间
sudo mkswap /swapfile
# 启用交换文件
sudo swapon /swapfile
# 验证交换空间是否已启用
sudo swapon --show
# 使交换文件在重启后仍然有效
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
😐 重新打包依旧死机,使用的 CPU核心数 也得限制下,两种设置方法:
- ① gradle.properties 中添加 org.gradle.workers.max=2。
- ② 直接在 命令行 中指定 最大并行工作线程数 ,如: ./gradlew build --max-workers=2
🙂 经过漫长的等待,终于打包成功了,是真的慢啊!!!不过只是备用,凑合着能用就行...
6. 打包系统镜像
🤡 装系统 和 配环境 非常 浪费时间,有时不小心把系统搞坏了,又得重头来一遍,非常要命。
打包一手当前的 系统镜像 在需要时恢复还是非常重要的,这里用到 开源 的 Systemback 工具,它主要用于 Linux 系统,可以帮助用户创建完整的系统备份,包括操作系统、应用程序、用户数据等。
😶大体过程是按照《Ubuntu基于整个环境打包docker镜像 将ubuntu打包为镜像》这篇文章来的~
执行下述命令安装 Systemback:
bash
sudo sh -c 'echo "deb [arch=amd64] http://mirrors.bwbot.org/ stable main" > /etc/apt/sources.list.d/systemback.list'
sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key 50B2C005A67B264F
sudo apt-get update
sudo apt-get install systemback
安装 make 编译器:
bash
sudo apt-get install make
安装 cdrtools:
bash
sudo apt install aria2
aria2c -s 10 https://nchc.dl.sourceforge.net/project/cdrtools/alpha/cdrtools-3.02a07.tar.gz
下载完执行下述命令解压:
bash
tar -xzvf cdrtools-3.02a07.tar.gz
cd cdrtools-3.02
编译安装然后启动:
bash
make
sudo apt update
sudo apt install build-essential
sudo make install
systemback-sustart
在弹出的对话框中选择账号,输入密码,点击OK:
点击 Live system create:
勾选包含用户数据,然后点击 Create new 进行创建:
经历漫长的等待后,弹出完成窗口:
此时,Live system create 可以看到我们创建的镜像 (比如这里是22G)
在 home 目录下可以找到这个镜像文件:
😳 直接就有 iso 文件了?但网上很多文章说 超过4G的镜像不能直接生成iso,然后得采用这种方法生成:
bash
# 创建文件夹 + 解压
mkdir sblive
tar -xf /home/systemback_live_2024-12-23.sblive -C sblive
# 重命名
mv sblive/syslinux/syslinux.cfg sblive/syslinux/isolinux.cfg
mv sblive/syslinux sblive/isolinux
# 生成iOS镜像
/opt/schily/bin/mkisofs -iso-level 3 -r -V sblive -cache-inodes -J -l -b isolinux/isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table -c isolinux/boot.cat -o sblive.iso sblive
有了iso镜像,然后就是做启动盘还原啦~