30、综合实战:从零构建一个嵌入式Android设备系统镜像
说实话,做嵌入式Android开发这么多年,我最怕听到的一句话就是——「你帮我打个镜像呗」。
很多人觉得这就是跑个make命令的事。但真正从零构建一个能跑起来的系统镜像,里面坑多着呢。今天我就带你走一遍完整的流程,把我踩过的坑、总结的经验都抖出来。
30.1 构建前的准备工作
先别急着敲命令。你想想看,如果地基没打好,后面盖再高的楼也得塌。
30.1.1 硬件平台确认
我习惯先把目标硬件的信息列清楚。不同的SoC、不同的外设,构建参数天差地别。
| 项目 | 说明 | 常见值 |
|---|---|---|
| SoC型号 | 主控芯片 | RK3588 / MT8395 / i.MX8M |
| 内存大小 | 决定dts配置 | 2GB / 4GB / 8GB |
| 存储介质 | eMMC / NAND / SD | eMMC 32GB |
| 显示接口 | HDMI / LVDS / MIPI | HDMI 1080p |
我的习惯: 拿到一块新板子,先拍张高清照片,把跳线、丝印都拍清楚。曾经有一次我搞混了板子版本,编译出来的镜像死活起不来,折腾了两天才发现是硬件版本不对。
30.1.2 构建环境搭建
嗯,这里要注意。Android的构建环境对Ubuntu版本有严格限制。我个人推荐Ubuntu 20.04 LTS,兼容性最好。
# 安装必要依赖
sudo apt-get update
sudo apt-get install -y git-core gnupg flex bison build-essential \
zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 \
libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev \
lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip
# 安装repo工具
mkdir -p ~/bin
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
export PATH=~/bin:$PATH
避坑指南: 我曾经在Ubuntu 22.04上编译Android 12,结果python版本冲突搞了整整一天。后来老老实实切回20.04,半小时搞定。有些坑没必要硬踩。
30.2 源码下载与分支选择
说白了,这一步就是去Google的仓库拉代码。但国内网络环境你懂的,得有点技巧。
30.2.1 选择合适的分支
我建议根据你的硬件驱动支持情况来选。别一味追新,稳定才是王道。
- android-12.0.0_r34 — 稳定,驱动支持广泛
- android-13.0.0_r41 — 新特性多,但部分老驱动不兼容
- android-14.0.0_r15 — 最新,适合新硬件
30.2.2 使用repo同步代码
# 初始化repo
mkdir android-12
cd android-12
repo init -u https://android.googlesource.com/platform/manifest \
-b android-12.0.0_r34
# 同步源码(建议用镜像站)
repo sync -j4 -c --no-tags
小技巧: 第一次同步建议加
--no-tags参数,能省不少带宽和时间。我一般晚上睡觉前开始同步,第二天早上起来就差不多了。
30.3 核心构建流程
代码拉下来了,接下来就是真正的构建环节。这里我画了一张流程图,帮你理清整体脉络。
30.3.1 内核编译
内核是系统的心脏。我一般先确认板子的defconfig文件,然后按需裁剪。
# 进入内核目录
cd kernel-5.10
# 配置内核
make ARCH=arm64 rockchip_defconfig
# 编译内核
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-android- \
Image.gz dtbs -j8
# 编译内核模块
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-android- \
modules -j8
关键点: 内核的dts文件一定要和你的硬件完全匹配。我曾经因为dts里GPIO编号写错一位,导致触摸屏完全没反应。排查了三天,最后发现是dts里一个引脚号错了。
30.3.2 Android系统编译
回到AOSP根目录,执行标准编译流程。
# 设置环境变量
source build/envsetup.sh
# 选择目标设备
lunch rk3588_evb7-userdebug
# 开始编译(根据机器配置调整线程数)
make -j16 2>&1 | tee build.log
我的经验: 第一次编译建议加
showcommands参数,能看到每一步的具体命令。出错了也好定位。另外,编译日志一定要保存,排查问题全靠它。
30.4 镜像打包与分区规划
编译完成后,生成的是散文件。我们需要把它们打包成可烧录的镜像。
30.4.1 分区表设计
分区表决定了你的系统怎么布局。我一般这样规划:
| 分区名 | 大小 | 内容 |
|---|---|---|
| boot | 64MB | 内核 + ramdisk |
| system | 2GB | 系统文件 |
| vendor | 512MB | 厂商驱动 |
| userdata | 剩余空间 | 用户数据 |
30.4.2 生成完整镜像
# 生成boot.img
mkbootimg --kernel kernel-5.10/arch/arm64/boot/Image.gz \
--ramdisk out/target/product/rk3588/ramdisk.img \
--cmdline "androidboot.hardware=rk3588" \
--base 0x00200000 \
--pagesize 4096 \
-o boot.img
# 生成super.img(Android 10+动态分区)
lpmake --metadata-size 65536 \
--super-name super \
--metadata-slots 2 \
--device super:4294967296 \
--group main:4294967296 \
--partition system:readonly:2147483648:main \
--partition vendor:readonly:536870912:main \
--image system=out/target/product/rk3588/system.img \
--image vendor=out/target/product/rk3588/vendor.img \
--sparse \
-o super.img
注意: 动态分区的大小计算一定要精确。我见过有人把super分区设小了,结果刷机后系统一直重启。分区大小建议留10%的余量。
30.5 烧录与调试
镜像做好了,接下来就是烧录到板子上。这一步看似简单,但最容易出幺蛾子。
30.5.1 烧录工具选择
- fastboot — 通用工具,适合大多数设备
- rkdeveloptool — Rockchip专用,支持Maskrom模式
- SP_Flash_Tool — MTK平台专用
30.5.2 烧录步骤
# 进入fastboot模式
adb reboot bootloader
# 烧录各分区
fastboot flash boot boot.img
fastboot flash super super.img
fastboot flash userdata userdata.img
# 擦除并重启
fastboot erase misc
fastboot reboot
调试技巧: 如果板子起不来,先别慌。接上串口看log,一般问题都出在dts配置或者驱动加载上。我习惯在板子上电前就把串口接好,这样能抓到完整的启动log。
30.6 常见问题与解决方案
最后,我把这些年遇到的高频问题整理一下,希望能帮你少走弯路。
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 内核panic | dts配置错误 | 检查GPIO、时钟、电源配置 |
| 系统反复重启 | 分区大小不足 | 重新计算分区表 |
| 触摸屏无响应 | 驱动未加载 | 检查内核模块是否编译 |
| WiFi无法连接 | 固件缺失 | 将WiFi固件放入vendor分区 |
构建嵌入式Android系统镜像,说白了就是一场「配置-编译-调试」的循环。别指望一次成功,也别怕失败。我刚开始做的时候,连续一周每天编译三四次,烧录到板子上看结果,再改再编。这个过程虽然枯燥,但每解决一个问题,你对整个系统的理解就深一层。
好了,动手试试吧。从今天开始,你也能从零构建一个属于自己的嵌入式Android系统镜像。
公众号:蓝海资料掘金营,微信deep3321