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 核心构建流程

代码拉下来了,接下来就是真正的构建环节。这里我画了一张流程图,帮你理清整体脉络。

嵌入式Android系统镜像构建流程 1. 配置内核 2. 编译Bootloader 3. 编译Android系统 4. 打包镜像文件 5. 烧录验证 内核配置 defconfig + dts U-Boot/ATF 板级初始化 AOSP编译 make -j8 镜像生成 boot.img / system.img 烧录测试 fastboot / rkdeveloptool

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