Ubuntu 16.04编译Android 6.0源码的优化实践
1. 环境准备:为什么选择Ubuntu 16.04 LTS?
三年前我在尝试编译Android 6.0源码时,发现大多数教程都基于Ubuntu 14.04环境。但当时16.04 LTS刚发布不久,作为长期支持版本,它在硬件兼容性和工具链更新方面有明显优势。实测表明,同样的硬件配置下,16.04的编译效率比14.04提升约20%,这主要得益于内核调度优化和文件系统改进。
物理机安装是另一个关键决策点。我曾用VMware虚拟机测试编译,不仅需要配置复杂的共享文件夹(源码目录大小超过30GB),编译过程中还频繁出现内存不足导致的进程崩溃。后来改用双系统方案,在ThinkPad P50(64GB内存+NVMe SSD)上完整编译仅需3小时,而虚拟机方案耗时超过15小时。
重要提示:/home分区至少分配120GB空间,Android 6.0完整编译后out目录会占用约80GB空间,加上源码本身和中间文件,实际需要更多空间缓冲。
2. 依赖管理:Ubuntu 16.04的特殊配置
2.1 软件源优化
阿里云镜像源确实能显著提升包下载速度,但需要注意16.04的代号是xenial而非示例中的quantal(这是12.10的代号)。正确的源配置应该是:
deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse2.2 关键依赖项
经过多次验证,以下依赖组合在16.04上最稳定:
sudo apt-get install -y git-core 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 m4 libsdl1.2-dev特别注意:
- libsdl1.2-dev必须保留,不能简化为libsdl-dev
- 需要显式指定i386架构的库(如lib32ncurses5-dev)
- ccache对二次编译加速至关重要
3. JDK版本陷阱与解决方案
Android 6.0要求OpenJDK 7,但Ubuntu 16.04默认仓库已移除了OpenJDK 7。通过PPA安装时要注意密钥验证:
sudo add-apt-repository ppa:openjdk-r/ppa sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EB9B1D8886F44E2A sudo apt-get update sudo apt-get install openjdk-7-jdk环境变量配置有个细节容易被忽略:在/etc/profile中设置JAVA_HOME时,实际路径需要根据CPU架构调整。对于x86_64机器应该是:
export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64而32位系统则需要改为:
export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-i386验证时不要只看java -version输出,还要检查javac版本:
javac -version两者必须都是1.7.x版本,否则会出现微妙的编译错误。
4. 源码处理与关键修改
4.1 源码合并校验
当使用cat合并分卷压缩包时,建议先用dd命令验证文件完整性:
dd if=Android6_r1.part1 of=test.bin bs=1M count=10 md5sum test.bin对比分卷压缩包内的校验值,确保网络传输没有出错。
4.2 ART编译问题修复
art/build/Android.common_build.mk的修改需要结合两个地方:
- 第75行修改条件判断:
ifeq ($(WITHOUT_HOST_CLANG),false)- 还需要在同一个文件的第230行附近添加:
CLANG := false这个问题的本质是Ubuntu 16.04默认的clang版本(3.8)与Android 6.0的编译系统存在兼容性问题。通过彻底禁用clang改用gcc可以避免复杂的版本降级操作。
5. 编译过程优化技巧
5.1 ccache配置
除了设置USE_CCACHE=1外,还需要调整缓存策略:
ccache -o compression=true ccache -o max_size=50G ccache -o sloppiness=include_file_mtime,include_file_ctime这样配置后,二次编译时间可以缩短60%以上。
5.2 并行编译参数
make -jN的参数选择有讲究:
- 首先获取CPU核心数:
nproc --all- 推荐计算公式:N = (核心数 × 2) + 2
- 监控系统负载,避免OOM:
watch -n 1 'free -m; ps -aux | grep make'5.3 内存管理
在16GB内存的机器上,建议添加交换文件:
sudo fallocate -l 8G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile编译完成后可以禁用:
sudo swapoff /swapfile sudo rm /swapfile6. 典型错误排查指南
6.1 dex2oat失败
错误特征:
FAILED: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.dex Error: Could not create the Java Virtual Machine.解决方案:
- 检查JAVA_HOME是否指向OpenJDK 7
- 修改build/core/dex_preopt.mk:
DEX2OAT := /usr/bin/java -jar $(DEX2OATD)改为:
DEX2OAT := /usr/lib/jvm/java-7-openjdk-amd64/bin/java -jar $(DEX2OATD)6.2 文件权限问题
错误特征:
/bin/bash: out/host/linux-x86/bin/acp: Permission denied解决方法:
chmod -R 755 out find . -name "*.sh" -exec chmod 755 {} \;6.3 Python版本冲突
Ubuntu 16.04默认Python3会导致部分脚本失败,临时切换版本:
sudo update-alternatives --config python选择Python 2.7后再重新编译。
7. 编译后验证与调试
成功编译后,建议运行以下检查:
# 检查镜像完整性 file out/target/product/generic/system.img # 验证内核版本 strings out/target/product/generic/ramdisk.img | grep Linux # 启动模拟器并获取日志 emulator -show-kernel -verbose adb logcat | grep -i error如果遇到模拟器黑屏,可以尝试:
emulator -gpu host -no-audio这通常是由于16.04的图形驱动兼容性问题导致。
8. 后续开发建议
- 使用repo工具管理源码修改:
repo init -u https://android.googlesource.com/platform/manifest -b android-6.0.1_r77 repo sync -j4- 增量编译技巧:
make -j8 showcommands | tee build.log这样可以保留完整的编译日志,方便问题定位。
- 模块化编译示例(仅编译Settings应用):
mmm packages/apps/Settings/这套环境经过我在三台不同配置的机器上验证(Intel i7/Xeon E5/Ryzen 7),最关键的教训是:所有路径必须全英文,JDK版本必须严格匹配,遇到错误先检查前序步骤是否完全正确。现在每次有新同事需要搭建环境,我都会让他们先完整阅读这篇指南,通常可以避免90%的常见问题。
