嵌入式Android 5.0编译环境搭建:Ubuntu版本选择与JDK配置避坑指南
1. 为什么这套老环境至今还有人折腾如果你手里正好有一块 tiny4412 的开发板或者正在跟着某套经典课程补嵌入式 Android 的底层知识那你大概率绕不开一个现实问题官方给的资料是十几年前的配套的 Ubuntu 版本、JDK 版本、编译工具链版本全都对不上现在的新系统。直接拿 Ubuntu 22.04 去编译 Android 5.0报错能刷满整个终端而且很多错误看起来毫无头绪。我自己前后在三台不同配置的机器上搭过这套环境踩的坑足够写满两页纸。这篇文章就是把这些坑一次性讲清楚从宿主机系统选型、依赖安装、源码目录结构、编译参数配置一直到镜像烧录和首次开机验证每一步都给出可复现的操作和背后的原因。目标读者是刚接触嵌入式 Android 的初学者以及需要维护老项目的工程师。整套流程走通之后你会得到一个能正常启动的 Android 5.0 系统并且理解每个环节为什么必须这么做。需要提前说明的是这套环境的核心矛盾在于新宿主机和老代码之间的兼容性。Android 5.0 的构建系统对 GCC 版本、Python 版本、JDK 版本都有硬性要求而这些要求在较新的 Ubuntu 上默认都不满足。所以整篇文章的主线就是如何在一个相对现代的 Ubuntu 上通过合理的版本管理和环境隔离把老代码编译出来。2. 宿主机系统与基础依赖的版本博弈2.1 Ubuntu 版本选择的取舍逻辑很多人第一反应是装最新版 Ubuntu觉得越新越稳定。但在这套环境里这个思路会直接把你带进沟里。Android 5.0 的构建脚本大量依赖 Python 2.7而 Ubuntu 20.04 之后默认只有 Python 3虽然可以手动装 Python 2但系统里一堆工具已经绑定了 Python 3强行切换会引发连锁问题。我的建议是直接用 Ubuntu 14.04 或 16.04 的 64 位版本。这两个版本默认就带 Python 2.7GCC 版本也在 4.8 到 5.4 之间正好落在 Android 5.0 能接受的范围内。如果你实在不想装老系统退而求其次可以用 Ubuntu 18.04但需要额外处理 Python 2 的安装和默认指向问题。宿主机版本Python 默认GCC 默认推荐程度主要风险Ubuntu 14.042.74.8强烈推荐软件源较老需换源Ubuntu 16.042.75.4推荐部分依赖需手动补Ubuntu 18.043.67.5可用需装 Python 2 并改默认Ubuntu 20.043.89.4不推荐兼容性问题极多装系统的时候有个细节要注意磁盘空间至少留 200GB。Android 5.0 完整源码加上编译产物轻松超过 100GB如果只编译内核和部分模块也要留 80GB 以上。我见过有人用 60GB 的虚拟机编译到一半空间不足清理起来非常麻烦。2.2 依赖包的完整安装清单系统装好之后第一件事是更新源并安装编译所需的依赖。这里我直接给出一份经过验证的完整清单你可以一次性复制执行sudo apt-get update sudo apt-get install -y git gnupg flex bison gperf build-essential \ zip curl zlib1g-dev gcc-multilib g-multilib libc6-dev-i386 \ lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache \ libgl1-mesa-dev libxml2-utils xsltproc unzip python2.7 \ openjdk-7-jdk这份清单里有几个包容易被忽略但非常关键。gcc-multilib和g-multilib是编译 32 位工具链用的缺了会在编译 host 工具时报错。lib32ncurses5-dev和lib32z-dev是 32 位库Android 5.0 的构建系统里有一部分工具是 32 位的。ccache是编译缓存工具能大幅加快二次编译速度强烈建议装上。JDK 这块要特别说一下。Android 5.0 官方要求的是 OpenJDK 7如果你装了 OpenJDK 8 或更高版本编译过程中会出现javac相关的错误。Ubuntu 14.04 的源里直接有openjdk-7-jdk16.04 之后的源里可能已经移除了需要手动添加 PPA 或者下载 deb 包安装。注意不要同时安装多个版本的 JDK 并让系统自动选择。装完之后用update-alternatives --config java确认当前使用的是 JDK 7否则编译到 Java 部分会莫名其妙失败。2.3 环境变量的配置细节依赖装完之后需要配置几个环境变量。编辑~/.bashrc在末尾加上export JAVA_HOME/usr/lib/jvm/java-7-openjdk-amd64 export PATH$JAVA_HOME/bin:$PATH export USE_CCACHE1 export CCACHE_DIR~/.ccacheJAVA_HOME必须指向 JDK 7 的安装目录这个路径在不同系统上可能略有差异用ls /usr/lib/jvm/确认一下。USE_CCACHE1开启编译缓存CCACHE_DIR指定缓存目录建议放在空间充足的磁盘上。配置完之后执行source ~/.bashrc然后用java -version验证输出是 1.7 版本。这一步看起来简单但我见过太多人因为 JDK 版本不对编译到一半才发现问题白白浪费几个小时。3. 源码获取与目录结构的整理3.1 源码包的获取与校验tiny4412 的 Android 5.0 源码通常由开发板厂商提供一般是一个或多个压缩包。拿到源码包之后第一件事是校验完整性。用md5sum或者sha1sum对比厂商给出的校验值确保下载过程中没有损坏。我遇到过压缩包解压到一半报错的情况重新下载才解决。解压的时候要注意目录结构。通常厂商会提供一个解压脚本或者要求你按特定顺序解压多个包。建议在一个空间充足的目录下操作比如/home/你的用户名/tiny4412。解压完成后顶层目录里应该能看到u-boot、kernel、android这几个子目录分别对应引导程序、内核和 Android 系统。如果厂商提供的是单个大包解压后目录结构可能不太一样需要根据实际情况调整。核心原则是确保android目录下有build、frameworks、packages这些标准目录这是判断源码是否完整的最快方法。3.2 源码目录的权限与属主处理解压出来的源码经常会有权限问题尤其是从 Windows 系统拷贝过来的包。执行编译前建议统一修改属主和权限sudo chown -R $USER:$USER /home/你的用户名/tiny4412 chmod -R 755 /home/你的用户名/tiny4412这一步不做的话编译过程中会出现各种Permission denied错误而且报错位置随机排查起来很费劲。统一改权限之后这类问题基本消失。另外要注意源码目录不要放在 NTFS 或 FAT32 分区上这些文件系统不支持 Linux 的权限模型和符号链接编译必然失败。必须放在 ext4 分区上。3.3 关键配置文件的修改在编译之前有几个配置文件需要根据实际情况修改。首先是build/core/version_defaults.mk里面定义了系统版本号一般不需要动。然后是device目录下对应开发板的配置文件tiny4412 通常在device/friendly-arm/tiny4412或类似路径下。需要重点检查的是BoardConfig.mk和AndroidProducts.mk确认里面的分区大小、内核路径、编译目标等参数和你的开发板匹配。如果厂商已经配置好了这一步可以跳过但建议至少浏览一遍了解各个参数的含义。还有一个容易忽略的点是build/core/combo/select.mk里的工具链路径。如果厂商用的是自定义工具链需要确认路径正确否则编译时会提示找不到gcc或g。4. 编译流程的完整拆解与参数说明4.1 初始化编译环境进入 android 源码根目录执行source build/envsetup.sh这个脚本会加载一系列编译相关的函数和变量是后续所有操作的基础。执行完之后可以用lunch命令查看可选的编译目标。tiny4412 通常会有一个类似tiny4412-eng或full_tiny4412-eng的选项。选择编译目标lunch full_tiny4412-eng这里的eng表示工程版带有 root 权限和调试工具适合开发阶段使用。另外还有user和userdebug两种模式user是发布版权限受限userdebug介于两者之间。初学阶段建议用eng方便调试。4.2 编译参数的优化配置正式编译之前可以根据机器配置调整并行编译的线程数。用nproc查看 CPU 核心数然后make -j8-j后面的数字建议设为核心数的 1 到 1.5 倍。设太大反而会因为内存不足导致编译中断设太小则编译速度慢。我一般用核心数加 2比如 8 核机器用-j10。如果内存不足 16GB建议加上-j4保守一点并且在编译过程中关掉其他占内存的程序。Android 5.0 完整编译对内存的需求不小尤其是链接阶段。编译时间方面在 8 核 16GB 的机器上首次完整编译大约需要 2 到 4 小时具体取决于磁盘速度和 CPU 性能。用 SSD 的话会快很多。二次编译如果开启了 ccache时间可以缩短到几十分钟。4.3 常见编译错误的定位与处理编译过程中最常见的错误集中在几个地方。第一个是 Python 相关错误通常表现为SyntaxError或ImportError根本原因是系统默认 Python 版本不对。解决办法是确认python命令指向 Python 2.7sudo ln -sf /usr/bin/python2.7 /usr/bin/python第二个是 JDK 相关错误表现为javac报错或找不到类。确认JAVA_HOME指向 JDK 7并且java -version输出正确。第三个是工具链错误表现为arm-linux-gcc: not found或类似提示。这通常是工具链路径没配置好检查BoardConfig.mk里的TARGET_TOOLS_PREFIX是否正确。第四个是内存不足导致的Killed错误。这种错误没有明显的堆栈信息只显示进程被杀死。解决办法是减少-j的数值或者增加 swap 空间。错误类型典型表现根本原因解决方向Python 错误SyntaxError / ImportError默认 Python 版本不对切换 python 指向 2.7JDK 错误javac 报错 / 找不到类JDK 版本不匹配确认使用 JDK 7工具链错误arm-linux-gcc not found工具链路径错误检查 TARGET_TOOLS_PREFIX内存不足进程被 Killed并行数过高降低 -j 数值或加 swap4.4 编译产物的位置与含义编译成功后产物主要在out/target/product/tiny4412/目录下。核心文件包括boot.img内核和 ramdisk 的打包镜像system.imgAndroid 系统分区镜像userdata.img用户数据分区镜像ramdisk.img内存文件系统镜像u-boot.bin引导程序如果单独编译了 u-boot这些镜像文件就是后续烧录到开发板上的内容。建议在编译完成后把整个out/target/product/tiny4412/目录备份一份这样即使源码被改乱也能快速恢复到一个可用的状态。5. 镜像烧录与首次开机验证5.1 烧录工具的安装与配置tiny4412 通常使用厂商提供的烧录工具在 Windows 下运行。需要先安装 USB 驱动然后用 USB 线连接开发板的 OTG 口和电脑。开发板进入烧录模式的方法一般是按住某个按键再上电具体操作参考开发板手册。烧录工具里需要指定各个镜像文件的路径对应关系如下分区镜像文件说明Bootloaderu-boot.bin引导程序Kernelboot.img内核与 ramdiskSystemsystem.imgAndroid 系统Userdatauserdata.img用户数据烧录之前确认开发板的拨码开关处于正确位置通常是拨到 eMMC 启动模式。烧录过程中不要断电否则可能导致开发板变砖需要用 SD 卡恢复。5.2 串口终端的连接与日志查看烧录完成后用串口线连接开发板的调试串口和电脑波特率一般设为 115200。打开串口终端工具上电后就能看到启动日志。首次启动会比较慢因为系统需要初始化用户数据分区可能要等几分钟。启动过程中如果卡住串口日志会给出线索。常见的卡住位置和原因卡在 u-boot 阶段引导程序有问题检查 u-boot 是否匹配开发板卡在内核启动阶段内核配置或设备树有问题卡在 Android 启动动画系统分区有问题或者文件系统损坏5.3 首次开机的验证要点系统正常启动后进入 Android 桌面需要验证几个关键功能。首先是触摸屏是否正常点击各个图标看响应是否准确。然后是显示是否正常检查分辨率是否正确有没有花屏或偏色。接着验证网络功能连接 WiFi 或者插网线看能否正常上网。最后验证 USB 功能插 U 盘看能否识别。这些基础功能都正常说明整个环境搭建成功。如果某个功能不正常先看串口日志有没有相关报错然后检查对应的驱动配置。嵌入式开发里硬件和软件是紧密耦合的很多问题需要两边一起排查。6. 几个容易翻车的细节和我的处理习惯6.1 编译中途断电后的恢复编译过程中如果断电或者强制关机重新编译时可能会遇到各种奇怪的错误。这是因为编译中间产物处于不一致的状态。我的处理习惯是先执行make clean清理然后重新编译。虽然会多花时间但比在错误状态下反复尝试要靠谱。如果make clean也报错可以直接删除out目录然后重新source build/envsetup.sh和lunch再编译。这样相当于完全重新开始但能保证状态干净。6.2 源码修改后的增量编译修改了某个模块的代码后不需要完整重新编译。进入对应模块的目录执行mm命令即可编译该模块。编译产物会自动更新到out目录对应位置。然后重新打包system.imgmake snod这个命令会快速重新生成 system 镜像不用走完整编译流程。对于频繁修改代码的调试阶段这个技巧能节省大量时间。6.3 磁盘空间不足的预防前面提到过磁盘空间的问题这里再强调一下。编译过程中out目录会迅速膨胀如果空间不足编译会以各种奇怪的方式失败。建议在编译前用df -h确认可用空间至少留 50GB 余量。如果空间实在紧张可以在编译完成后删除out目录里的中间产物只保留最终的镜像文件。具体做法是保留out/target/product/tiny4412/下的*.img文件其余可以清理。6.4 版本管理的建议这套老环境的搭建过程比较繁琐建议把关键步骤和配置记录下来形成自己的文档。源码和编译产物也要做好备份尤其是编译成功后的out目录。我一般会在编译成功后打个压缩包标注日期和配置信息这样以后需要重新搭建时可以直接恢复不用从头再来。另外如果团队里有多个人需要这套环境可以考虑把配置好的虚拟机镜像共享出来这样每个人都能快速上手避免重复踩坑。虚拟机的好处是环境隔离不会影响宿主机而且可以随时快照回滚。整套流程走下来最耗时的其实是编译环节真正需要手动操作的步骤并不多。关键是要理解每个步骤背后的原因这样遇到问题时才能快速定位。嵌入式 Android 的环境搭建没有捷径但把原理搞清楚了后面调试驱动和应用就会顺畅很多。